=0;t--)95!==(n=r[t]).marker&&42!==n.marker||-1!==n.end&&(s=r[n.end],a=t>0&&r[t-1].end===n.end+1&&r[t-1].marker===n.marker&&r[t-1].token===n.token-1&&r[n.end+1].token===s.token+1,i=String.fromCharCode(n.marker),(o=e.tokens[n.token]).type=a?"strong_open":"em_open",o.tag=a?"strong":"em",o.nesting=1,o.markup=a?i+i:i,o.content="",(o=e.tokens[s.token]).type=a?"strong_close":"em_close",o.tag=a?"strong":"em",o.nesting=-1,o.markup=a?i+i:i,o.content="",a&&(e.tokens[r[t-1].token].content="",e.tokens[r[n.end+1].token].content="",t--))}var Ue={tokenize:function(e,r){var t,n,s=e.pos,o=e.src.charCodeAt(s);if(r)return!1;if(95!==o&&42!==o)return!1;for(n=e.scanDelims(e.pos,42===o),t=0;t\x00-\x20]*)$/,We=Ae.HTML_TAG_RE;var Ye=w.has,Ke=w.isValidEntityCode,Qe=w.fromCodePoint,Xe=/^((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,er=/^&([a-z][a-z0-9]{1,31});/i;function rr(e,r){var t,n,s,o,i,a,c,l,u={},p=r.length;if(p){var h=0,f=-2,d=[];for(t=0;ti;n-=d[n]+1)if((o=r[n]).marker===s.marker&&o.open&&o.end<0&&(c=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(c=!0)),!c)){l=n>0&&!r[n-1].open?d[n-1]+1:0,d[t]=t-n+l,d[n]=l,s.open=!1,o.end=t,o.close=!1,a=-1,f=-2;break}-1!==a&&(u[s.marker][(s.open?3:0)+(s.length||0)%3]=a)}}}var tr=w.isWhiteSpace,nr=w.isPunctChar,sr=w.isMdAsciiPunct;function or(e,r,t,n){this.src=e,this.env=t,this.md=r,this.tokens=n,this.tokens_meta=Array(n.length),this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache={},this.delimiters=[],this._prev_delimiters=[],this.backticks={},this.backticksScanned=!1,this.linkLevel=0}or.prototype.pushPending=function(){var e=new oe("text","",0);return e.content=this.pending,e.level=this.pendingLevel,this.tokens.push(e),this.pending="",e},or.prototype.push=function(e,r,t){this.pending&&this.pushPending();var n=new oe(e,r,t),s=null;return t<0&&(this.level--,this.delimiters=this._prev_delimiters.pop()),n.level=this.level,t>0&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],s={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(n),this.tokens_meta.push(s),n},or.prototype.scanDelims=function(e,r){var t,n,s,o,i,a,c,l,u,p=e,h=!0,f=!0,d=this.posMax,m=this.src.charCodeAt(e);for(t=e>0?this.src.charCodeAt(e-1):32;p0)&&(!((t=e.pos)+3>e.posMax)&&(58===e.src.charCodeAt(t)&&(47===e.src.charCodeAt(t+1)&&(47===e.src.charCodeAt(t+2)&&(!!(n=e.pending.match(Ie))&&(s=n[1],!!(o=e.md.linkify.matchAtStart(e.src.slice(t-s.length)))&&(i=(i=o.url).replace(/\*+$/,""),a=e.md.normalizeLink(i),!!e.md.validateLink(a)&&(r||(e.pending=e.pending.slice(0,-s.length),(c=e.push("link_open","a",1)).attrs=[["href",a]],c.markup="linkify",c.info="auto",(c=e.push("text","",0)).content=e.md.normalizeLinkText(i),(c=e.push("link_close","a",-1)).markup="linkify",c.info="auto"),e.pos+=i.length-s.length,!0)))))))))}],["newline",function(e,r){var t,n,s,o=e.pos;if(10!==e.src.charCodeAt(o))return!1;if(t=e.pending.length-1,n=e.posMax,!r)if(t>=0&&32===e.pending.charCodeAt(t))if(t>=1&&32===e.pending.charCodeAt(t-1)){for(s=t-1;s>=1&&32===e.pending.charCodeAt(s-1);)s--;e.pending=e.pending.slice(0,s),e.push("hardbreak","br",0)}else e.pending=e.pending.slice(0,-1),e.push("softbreak","br",0);else e.push("softbreak","br",0);for(o++;o=c)return!1;if(10===(t=e.src.charCodeAt(a))){for(r||e.push("hardbreak","br",0),a++;a=55296&&t<=56319&&a+1=56320&&n<=57343&&(o+=e.src[a+1],a++),s="\\"+o,r||(i=e.push("text_special","",0),t<256&&0!==Be[t]?i.content=o:i.content=s,i.markup=s,i.info="escape"),e.pos=a+1,!0}],["backticks",function(e,r){var t,n,s,o,i,a,c,l,u=e.pos;if(96!==e.src.charCodeAt(u))return!1;for(t=u,u++,n=e.posMax;u=f)return!1;if(d=a,(c=e.md.helpers.parseLinkDestination(e.src,a,e.posMax)).ok){for(u=e.md.normalizeLink(c.str),e.md.validateLink(u)?a=c.pos:u="",d=a;a=f||41!==e.src.charCodeAt(a))&&(m=!0),a++}if(m){if(void 0===e.env.references)return!1;if(a=0?s=e.src.slice(d,a++):a=o+1):a=o+1,s||(s=e.src.slice(i,o)),!(l=e.env.references[Ve(s)]))return e.pos=h,!1;u=l.href,p=l.title}return r||(e.pos=i,e.posMax=o,e.push("link_open","a",1).attrs=t=[["href",u]],p&&t.push(["title",p]),e.linkLevel++,e.md.inline.tokenize(e),e.linkLevel--,e.push("link_close","a",-1)),e.pos=a,e.posMax=f,!0}],["image",function(e,r){var t,n,s,o,i,a,c,l,u,p,h,f,d,m="",g=e.pos,_=e.posMax;if(33!==e.src.charCodeAt(e.pos))return!1;if(91!==e.src.charCodeAt(e.pos+1))return!1;if(a=e.pos+2,(i=e.md.helpers.parseLinkLabel(e,e.pos+1,!1))<0)return!1;if((c=i+1)<_&&40===e.src.charCodeAt(c)){for(c++;c<_&&(n=e.src.charCodeAt(c),Ge(n)||10===n);c++);if(c>=_)return!1;for(d=c,(u=e.md.helpers.parseLinkDestination(e.src,c,e.posMax)).ok&&(m=e.md.normalizeLink(u.str),e.md.validateLink(m)?c=u.pos:m=""),d=c;c<_&&(n=e.src.charCodeAt(c),Ge(n)||10===n);c++);if(u=e.md.helpers.parseLinkTitle(e.src,c,e.posMax),c<_&&d!==c&&u.ok)for(p=u.str,c=u.pos;c<_&&(n=e.src.charCodeAt(c),Ge(n)||10===n);c++);else p="";if(c>=_||41!==e.src.charCodeAt(c))return e.pos=g,!1;c++}else{if(void 0===e.env.references)return!1;if(c<_&&91===e.src.charCodeAt(c)?(d=c+1,(c=e.md.helpers.parseLinkLabel(e,c))>=0?o=e.src.slice(d,c++):c=i+1):c=i+1,o||(o=e.src.slice(a,i)),!(l=e.env.references[$e(o)]))return e.pos=g,!1;m=l.href,p=l.title}return r||(s=e.src.slice(a,i),e.md.inline.parse(s,e.md,e.env,f=[]),(h=e.push("image","img",0)).attrs=t=[["src",m],["alt",""]],h.children=f,h.content=s,p&&t.push(["title",p])),e.pos=c,e.posMax=_,!0}],["autolink",function(e,r){var t,n,s,o,i,a,c=e.pos;if(60!==e.src.charCodeAt(c))return!1;for(i=e.pos,a=e.posMax;;){if(++c>=a)return!1;if(60===(o=e.src.charCodeAt(c)))return!1;if(62===o)break}return t=e.src.slice(i+1,c),Je.test(t)?(n=e.md.normalizeLink(t),!!e.md.validateLink(n)&&(r||((s=e.push("link_open","a",1)).attrs=[["href",n]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(t),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=t.length+2,!0)):!!He.test(t)&&(n=e.md.normalizeLink("mailto:"+t),!!e.md.validateLink(n)&&(r||((s=e.push("link_open","a",1)).attrs=[["href",n]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(t),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=t.length+2,!0))}],["html_inline",function(e,r){var t,n,s,o,i,a=e.pos;return!!e.md.options.html&&(s=e.posMax,!(60!==e.src.charCodeAt(a)||a+2>=s)&&(!(33!==(t=e.src.charCodeAt(a+1))&&63!==t&&47!==t&&!function(e){var r=32|e;return r>=97&&r<=122}(t))&&(!!(n=e.src.slice(a).match(We))&&(r||((o=e.push("html_inline","",0)).content=e.src.slice(a,a+n[0].length),i=o.content,/^\s]/i.test(i)&&e.linkLevel++,function(e){return/^<\/a\s*>/i.test(e)}(o.content)&&e.linkLevel--),e.pos+=n[0].length,!0))))}],["entity",function(e,t){var n,s,o,i=e.pos,a=e.posMax;if(38!==e.src.charCodeAt(i))return!1;if(i+1>=a)return!1;if(35===e.src.charCodeAt(i+1)){if(s=e.src.slice(i).match(Xe))return t||(n="x"===s[1][0].toLowerCase()?parseInt(s[1].slice(1),16):parseInt(s[1],10),(o=e.push("text_special","",0)).content=Ke(n)?Qe(n):Qe(65533),o.markup=s[0],o.info="entity"),e.pos+=s[0].length,!0}else if((s=e.src.slice(i).match(er))&&Ye(r,s[1]))return t||((o=e.push("text_special","",0)).content=r[s[1]],o.markup=s[0],o.info="entity"),e.pos+=s[0].length,!0;return!1}]],cr=[["balance_pairs",function(e){var r,t=e.tokens_meta,n=e.tokens_meta.length;for(rr(0,e.delimiters),r=0;r0&&n++,"text"===s[r].type&&r+1=o)break}else e.pending+=e.src[e.pos++]}e.pending&&e.pushPending()},lr.prototype.parse=function(e,r,t,n){var s,o,i,a=new this.State(e,r,t,n);for(this.tokenize(a),i=(o=this.ruler2.getRules("")).length,s=0;s=3&&":"===e[r-3]||r>=3&&"/"===e[r-3]?0:n.match(t.re.no_http)[0].length:0}},"mailto:":{validate:function(e,r,t){var n=e.slice(r);return t.re.mailto||(t.re.mailto=new RegExp("^"+t.re.src_email_name+"@"+t.re.src_host_strict,"i")),t.re.mailto.test(n)?n.match(t.re.mailto)[0].length:0}}},_r="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|\u0440\u0444".split("|");function kr(e){var r=e.re=function(e){var r={};return e=e||{},r.src_Any=y.source,r.src_Cc=A.source,r.src_Z=x.source,r.src_P=t.source,r.src_ZPCc=[r.src_Z,r.src_P,r.src_Cc].join("|"),r.src_ZCc=[r.src_Z,r.src_Cc].join("|"),r.src_pseudo_letter="(?:(?![><\uff5c]|"+r.src_ZPCc+")"+r.src_Any+")",r.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",r.src_auth="(?:(?:(?!"+r.src_ZCc+"|[@/\\[\\]()]).)+@)?",r.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",r.src_host_terminator="(?=$|[><\uff5c]|"+r.src_ZPCc+")(?!"+(e["---"]?"-(?!--)|":"-|")+"_|:\\d|\\.-|\\.(?!$|"+r.src_ZPCc+"))",r.src_path="(?:[/?#](?:(?!"+r.src_ZCc+"|[><\uff5c]|[()[\\]{}.,\"'?!\\-;]).|\\[(?:(?!"+r.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+r.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+r.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+r.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+r.src_ZCc+"|[']).)+\\'|\\'(?="+r.src_pseudo_letter+"|[-])|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+r.src_ZCc+"|[.]|$)|"+(e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+",(?!"+r.src_ZCc+"|$)|;(?!"+r.src_ZCc+"|$)|\\!+(?!"+r.src_ZCc+"|[!]|$)|\\?(?!"+r.src_ZCc+"|[?]|$))+|\\/)?",r.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',r.src_xn="xn--[a-z0-9\\-]{1,59}",r.src_domain_root="(?:"+r.src_xn+"|"+r.src_pseudo_letter+"{1,63})",r.src_domain="(?:"+r.src_xn+"|(?:"+r.src_pseudo_letter+")|(?:"+r.src_pseudo_letter+"(?:-|"+r.src_pseudo_letter+"){0,61}"+r.src_pseudo_letter+"))",r.src_host="(?:(?:(?:(?:"+r.src_domain+")\\.)*"+r.src_domain+"))",r.tpl_host_fuzzy="(?:"+r.src_ip4+"|(?:(?:(?:"+r.src_domain+")\\.)+(?:%TLDS%)))",r.tpl_host_no_ip_fuzzy="(?:(?:(?:"+r.src_domain+")\\.)+(?:%TLDS%))",r.src_host_strict=r.src_host+r.src_host_terminator,r.tpl_host_fuzzy_strict=r.tpl_host_fuzzy+r.src_host_terminator,r.src_host_port_strict=r.src_host+r.src_port+r.src_host_terminator,r.tpl_host_port_fuzzy_strict=r.tpl_host_fuzzy+r.src_port+r.src_host_terminator,r.tpl_host_port_no_ip_fuzzy_strict=r.tpl_host_no_ip_fuzzy+r.src_port+r.src_host_terminator,r.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+r.src_ZPCc+"|>|$))",r.tpl_email_fuzzy='(^|[><\uff5c]|"|\\(|'+r.src_ZCc+")("+r.src_email_name+"@"+r.tpl_host_fuzzy_strict+")",r.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+r.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+r.tpl_host_port_fuzzy_strict+r.src_path+")",r.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+r.src_ZPCc+"))((?![$+<=>^`|\uff5c])"+r.tpl_host_port_no_ip_fuzzy_strict+r.src_path+")",r}(e.__opts__),n=e.__tlds__.slice();function s(e){return e.replace("%TLDS%",r.src_tlds)}e.onCompile(),e.__tlds_replaced__||n.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),n.push(r.src_xn),r.src_tlds=n.join("|"),r.email_fuzzy=RegExp(s(r.tpl_email_fuzzy),"i"),r.link_fuzzy=RegExp(s(r.tpl_link_fuzzy),"i"),r.link_no_ip_fuzzy=RegExp(s(r.tpl_link_no_ip_fuzzy),"i"),r.host_fuzzy_test=RegExp(s(r.tpl_host_fuzzy_test),"i");var o=[];function i(e,r){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+r)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(r){var t=e.__schemas__[r];if(null!==t){var n={validate:null,link:null};if(e.__compiled__[r]=n,"[object Object]"===hr(t))return!function(e){return"[object RegExp]"===hr(e)}(t.validate)?fr(t.validate)?n.validate=t.validate:i(r,t):n.validate=function(e){return function(r,t){var n=r.slice(t);return e.test(n)?n.match(e)[0].length:0}}(t.validate),void(fr(t.normalize)?n.normalize=t.normalize:t.normalize?i(r,t):n.normalize=function(e,r){r.normalize(e)});!function(e){return"[object String]"===hr(e)}(t)?i(r,t):o.push(r)}})),o.forEach((function(r){e.__compiled__[e.__schemas__[r]]&&(e.__compiled__[r].validate=e.__compiled__[e.__schemas__[r]].validate,e.__compiled__[r].normalize=e.__compiled__[e.__schemas__[r]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,r){r.normalize(e)}};var a=Object.keys(e.__compiled__).filter((function(r){return r.length>0&&e.__compiled__[r]})).map(dr).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><\uff5c]|"+r.src_ZPCc+"))("+a+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><\uff5c]|"+r.src_ZPCc+"))("+a+")","ig"),e.re.schema_at_start=RegExp("^"+e.re.schema_search.source,"i"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function br(e,r){var t=e.__index__,n=e.__last_index__,s=e.__text_cache__.slice(t,n);this.schema=e.__schema__.toLowerCase(),this.index=t+r,this.lastIndex=n+r,this.raw=s,this.text=s,this.url=s}function vr(e,r){var t=new br(e,r);return e.__compiled__[t.schema].normalize(t,e),t}function Cr(e,r){if(!(this instanceof Cr))return new Cr(e,r);var t;r||(t=e,Object.keys(t||{}).reduce((function(e,r){return e||mr.hasOwnProperty(r)}),!1)&&(r=e,e={})),this.__opts__=pr({},mr,r),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=pr({},gr,e),this.__compiled__={},this.__tlds__=_r,this.__tlds_replaced__=!1,this.re={},kr(this)}Cr.prototype.add=function(e,r){return this.__schemas__[e]=r,kr(this),this},Cr.prototype.set=function(e){return this.__opts__=pr(this.__opts__,e),this},Cr.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var r,t,n,s,o,i,a,c;if(this.re.schema_test.test(e))for((a=this.re.schema_search).lastIndex=0;null!==(r=a.exec(e));)if(s=this.testSchemaAt(e,r[2],a.lastIndex)){this.__schema__=r[2],this.__index__=r.index+r[1].length,this.__last_index__=r.index+r[0].length+s;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(c=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||c=0&&null!==(n=e.match(this.re.email_fuzzy))&&(o=n.index+n[1].length,i=n.index+n[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=i)),this.__index__>=0},Cr.prototype.pretest=function(e){return this.re.pretest.test(e)},Cr.prototype.testSchemaAt=function(e,r,t){return this.__compiled__[r.toLowerCase()]?this.__compiled__[r.toLowerCase()].validate(e,t,this):0},Cr.prototype.match=function(e){var r=0,t=[];this.__index__>=0&&this.__text_cache__===e&&(t.push(vr(this,r)),r=this.__last_index__);for(var n=r?e.slice(r):e;this.test(n);)t.push(vr(this,r)),n=n.slice(this.__last_index__),r+=this.__last_index__;return t.length?t:null},Cr.prototype.matchAtStart=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return null;var r=this.re.schema_at_start.exec(e);if(!r)return null;var t=this.testSchemaAt(e,r[2],r[0].length);return t?(this.__schema__=r[2],this.__index__=r.index+r[1].length,this.__last_index__=r.index+r[0].length+t,vr(this,0)):null},Cr.prototype.tlds=function(e,r){return e=Array.isArray(e)?e:[e],r?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,r,t){return e!==t[r-1]})).reverse(),kr(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,kr(this),this)},Cr.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},Cr.prototype.onCompile=function(){};var yr=Cr,Ar=2147483647,xr=36,Dr=/^xn--/,wr=/[^\x20-\x7E]/,Er=/[\x2E\u3002\uFF0E\uFF61]/g,qr={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},Sr=Math.floor,Fr=String.fromCharCode;
+/*! https://mths.be/punycode v1.4.1 by @mathias */function Lr(e){throw new RangeError(qr[e])}function zr(e,r){for(var t=e.length,n=[];t--;)n[t]=r(e[t]);return n}function Tr(e,r){var t=e.split("@"),n="";return t.length>1&&(n=t[0]+"@",e=t[1]),n+zr((e=e.replace(Er,".")).split("."),r).join(".")}function Ir(e){for(var r,t,n=[],s=0,o=e.length;s=55296&&r<=56319&&s65535&&(r+=Fr((e-=65536)>>>10&1023|55296),e=56320|1023&e),r+=Fr(e)})).join("")}function Rr(e,r){return e+22+75*(e<26)-((0!=r)<<5)}function Br(e,r,t){var n=0;for(e=t?Sr(e/700):e>>1,e+=Sr(e/r);e>455;n+=xr)e=Sr(e/35);return Sr(n+36*e/(e+38))}function Nr(e){var r,t,n,s,o,i,a,c,l,u,p,h=[],f=e.length,d=0,m=128,g=72;for((t=e.lastIndexOf("-"))<0&&(t=0),n=0;n=128&&Lr("not-basic"),h.push(e.charCodeAt(n));for(s=t>0?t+1:0;s=f&&Lr("invalid-input"),((c=(p=e.charCodeAt(s++))-48<10?p-22:p-65<26?p-65:p-97<26?p-97:xr)>=xr||c>Sr((Ar-d)/i))&&Lr("overflow"),d+=c*i,!(c<(l=a<=g?1:a>=g+26?26:a-g));a+=xr)i>Sr(Ar/(u=xr-l))&&Lr("overflow"),i*=u;g=Br(d-o,r=h.length+1,0==o),Sr(d/r)>Ar-m&&Lr("overflow"),m+=Sr(d/r),d%=r,h.splice(d++,0,m)}return Mr(h)}function Or(e){var r,t,n,s,o,i,a,c,l,u,p,h,f,d,m,g=[];for(h=(e=Ir(e)).length,r=128,t=0,o=72,i=0;i=r&&pSr((Ar-t)/(f=n+1))&&Lr("overflow"),t+=(a-r)*f,r=a,i=0;iAr&&Lr("overflow"),p==r){for(c=t,l=xr;!(c<(u=l<=o?1:l>=o+26?26:l-o));l+=xr)m=c-u,d=xr-u,g.push(Fr(Rr(u+m%d,0))),c=Sr(m/d);g.push(Fr(Rr(c,0))),o=Br(t,f,n==s),t=0,++n}++t,++r}return g.join("")}function Pr(e){return Tr(e,(function(e){return Dr.test(e)?Nr(e.slice(4).toLowerCase()):e}))}function jr(e){return Tr(e,(function(e){return wr.test(e)?"xn--"+Or(e):e}))}var Ur="1.4.1",Vr={decode:Ir,encode:Mr},Zr={version:Ur,ucs2:Vr,toASCII:jr,toUnicode:Pr,encode:Or,decode:Nr},$r=e(Object.freeze({__proto__:null,decode:Nr,encode:Or,toUnicode:Pr,toASCII:jr,version:Ur,ucs2:Vr,default:Zr})),Gr={default:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:100},components:{core:{},block:{},inline:{}}},zero:{options:{html:!1,xhtmlOut:!1,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline","text_join"]},block:{rules:["paragraph"]},inline:{rules:["text"],rules2:["balance_pairs","fragments_join"]}}},commonmark:{options:{html:!0,xhtmlOut:!0,breaks:!1,langPrefix:"language-",linkify:!1,typographer:!1,quotes:"\u201c\u201d\u2018\u2019",highlight:null,maxNesting:20},components:{core:{rules:["normalize","block","inline","text_join"]},block:{rules:["blockquote","code","fence","heading","hr","html_block","lheading","list","reference","paragraph"]},inline:{rules:["autolink","backticks","emphasis","entity","escape","html_inline","image","link","newline","text"],rules2:["balance_pairs","emphasis","fragments_join"]}}}},Hr=/^(vbscript|javascript|file|data):/,Jr=/^data:image\/(gif|png|jpeg|webp);/;function Wr(e){var r=e.trim().toLowerCase();return!Hr.test(r)||!!Jr.test(r)}var Yr=["http:","https:","mailto:"];function Kr(e){var r=C.parse(e,!0);if(r.hostname&&(!r.protocol||Yr.indexOf(r.protocol)>=0))try{r.hostname=$r.toASCII(r.hostname)}catch(e){}return C.encode(C.format(r))}function Qr(e){var r=C.parse(e,!0);if(r.hostname&&(!r.protocol||Yr.indexOf(r.protocol)>=0))try{r.hostname=$r.toUnicode(r.hostname)}catch(e){}return C.decode(C.format(r),C.decode.defaultChars+"%")}function Xr(e,r){if(!(this instanceof Xr))return new Xr(e,r);r||w.isString(e)||(r=e||{},e="default"),this.inline=new ur,this.block=new ze,this.core=new ue,this.renderer=new B,this.linkify=new yr,this.validateLink=Wr,this.normalizeLink=Kr,this.normalizeLinkText=Qr,this.utils=w,this.helpers=w.assign({},L),this.options={},this.configure(e),r&&this.set(r)}return Xr.prototype.set=function(e){return w.assign(this.options,e),this},Xr.prototype.configure=function(e){var r,t=this;if(w.isString(e)&&!(e=Gr[r=e]))throw new Error('Wrong `markdown-it` preset "'+r+'", check name');if(!e)throw new Error("Wrong `markdown-it` preset, can't be empty");return e.options&&t.set(e.options),e.components&&Object.keys(e.components).forEach((function(r){e.components[r].rules&&t[r].ruler.enableOnly(e.components[r].rules),e.components[r].rules2&&t[r].ruler2.enableOnly(e.components[r].rules2)})),this},Xr.prototype.enable=function(e,r){var t=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(r){t=t.concat(this[r].ruler.enable(e,!0))}),this),t=t.concat(this.inline.ruler2.enable(e,!0));var n=e.filter((function(e){return t.indexOf(e)<0}));if(n.length&&!r)throw new Error("MarkdownIt. Failed to enable unknown rule(s): "+n);return this},Xr.prototype.disable=function(e,r){var t=[];Array.isArray(e)||(e=[e]),["core","block","inline"].forEach((function(r){t=t.concat(this[r].ruler.disable(e,!0))}),this),t=t.concat(this.inline.ruler2.disable(e,!0));var n=e.filter((function(e){return t.indexOf(e)<0}));if(n.length&&!r)throw new Error("MarkdownIt. Failed to disable unknown rule(s): "+n);return this},Xr.prototype.use=function(e){var r=[this].concat(Array.prototype.slice.call(arguments,1));return e.apply(e,r),this},Xr.prototype.parse=function(e,r){if("string"!=typeof e)throw new Error("Input data should be a String");var t=new this.core.State(e,this,r);return this.core.process(t),t.tokens},Xr.prototype.render=function(e,r){return r=r||{},this.renderer.render(this.parse(e,r),this.options,r)},Xr.prototype.parseInline=function(e,r){var t=new this.core.State(e,this,r);return t.inlineMode=!0,this.core.process(t),t.tokens},Xr.prototype.renderInline=function(e,r){return r=r||{},this.renderer.render(this.parseInline(e,r),this.options,r)},Xr}));
diff --git a/qubership-apihub-service/static/templates/single_page.html b/qubership-apihub-service/static/templates/single_page.html
new file mode 100644
index 0000000..addbfeb
--- /dev/null
+++ b/qubership-apihub-service/static/templates/single_page.html
@@ -0,0 +1,39 @@
+
+
+
+
+ %s
+
+
+
+
+
+
+
+
+
+
diff --git a/qubership-apihub-service/tests/ValidatePackageOperations_test.go b/qubership-apihub-service/tests/ValidatePackageOperations_test.go
new file mode 100644
index 0000000..d59b27b
--- /dev/null
+++ b/qubership-apihub-service/tests/ValidatePackageOperations_test.go
@@ -0,0 +1,98 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tests
+
+import (
+ "testing"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/utils"
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/view"
+)
+
+func TestValidateObjectErrors(t *testing.T) {
+ var updateOperationGroupReqNil view.UpdateOperationGroupReq
+ var groupOperationsNil *[]view.GroupOperations = nil
+ updateOperationGroupReqNil.Operations = groupOperationsNil
+ updateOperationGroupReqExpectedErrorNil := "Required parameters are missing: [0]operations.operationId, [1]operations.operationId"
+ if err := utils.ValidateObject(updateOperationGroupReqNil); err != nil {
+ if updateOperationGroupReqExpectedErrorNil != err.Error() {
+ t.Fatalf("UpdateOperationGroupReq Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var updateOperationGroupReq view.UpdateOperationGroupReq
+ var groupOperations = make([]view.GroupOperations, 2)
+ updateOperationGroupReq.Operations = &groupOperations
+ updateOperationGroupReqExpectedError := "Required parameters are missing: operations[0].operationId, operations[1].operationId"
+ if err := utils.ValidateObject(updateOperationGroupReq); err != nil {
+ if updateOperationGroupReqExpectedError != err.Error() {
+ t.Fatalf("UpdateOperationGroupReq Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var packageOperationsFile view.PackageOperationsFile
+ var operations = make([]view.Operation, 2)
+ packageOperationsFile.Operations = operations
+ packageOperationsFileExpectedError := "Required parameters are missing: operations[0].operationId, operations[0].title, operations[0].apiType, operations[0].dataHash, operations[0].apiKind, operations[0].metadata, operations[0].searchScopes, operations[0].apiAudience, operations[1].operationId, operations[1].title, operations[1].apiType, operations[1].dataHash, operations[1].apiKind, operations[1].metadata, operations[1].searchScopes, operations[1].apiAudience"
+ if err := utils.ValidateObject(packageOperationsFile); err != nil {
+ if packageOperationsFileExpectedError != err.Error() {
+ t.Fatalf("Package Operations File Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var packageInfoFile view.PackageInfoFile
+ info := view.MakeChangelogInfoFileView(packageInfoFile)
+ packageInfoFileExpectedError := "Required parameters are missing: packageId, version, previousVersionPackageId, previousVersion"
+ if err := utils.ValidateObject(info); err != nil {
+ if packageInfoFileExpectedError != err.Error() {
+ t.Fatalf("Package Info File Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var packageComparisonsFile view.PackageComparisonsFile
+ var versionComparison = make([]view.VersionComparison, 2)
+ var operationTypes = make([]view.OperationType, 2)
+ versionComparison[0].OperationTypes = operationTypes
+ versionComparison[1].OperationTypes = operationTypes
+ packageComparisonsFile.Comparisons = versionComparison
+ packageComparisonsFileExpectedError := "Required parameters are missing: comparisons[0].operationTypes[0].apiType, comparisons[0].operationTypes[1].apiType, comparisons[1].operationTypes[0].apiType, comparisons[1].operationTypes[1].apiType"
+ if err := utils.ValidateObject(packageComparisonsFile); err != nil {
+ if packageComparisonsFileExpectedError != err.Error() {
+ t.Fatalf("Package Comparisons File Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var builderNotificationsFile view.BuilderNotificationsFile
+ var builderNotification = make([]view.BuilderNotification, 2)
+ builderNotificationsFile.Notifications = builderNotification
+ //no required params. empty error expected
+ builderNotificationsFileExpectedError := ""
+ if err := utils.ValidateObject(builderNotificationsFile); err != nil {
+ if builderNotificationsFileExpectedError != err.Error() {
+ t.Fatalf("Builder Notifications File Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+ var packageDocumentsFile view.PackageDocumentsFile
+ var packageDocument = make([]view.PackageDocument, 2)
+ packageDocumentsFile.Documents = packageDocument
+ packageDocumentsFileExpectedError := "Required parameters are missing: documents[0].fileId, documents[0].type, documents[0].slug, documents[0].title, documents[0].operationIds, documents[0].filename, documents[1].fileId, documents[1].type, documents[1].slug, documents[1].title, documents[1].operationIds, documents[1].filename"
+ if err := utils.ValidateObject(packageDocumentsFile); err != nil {
+ if packageDocumentsFileExpectedError != err.Error() {
+ t.Fatalf("Package Documents File Validation errors test is failed. Actual error: %v", err.Error())
+ }
+ }
+
+}
diff --git a/qubership-apihub-service/utils/ArrayUtils.go b/qubership-apihub-service/utils/ArrayUtils.go
new file mode 100644
index 0000000..d2cba01
--- /dev/null
+++ b/qubership-apihub-service/utils/ArrayUtils.go
@@ -0,0 +1,45 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+func SliceIndex(slice []string, val string) int {
+ for i, v := range slice {
+ if v == val {
+ return i
+ }
+ }
+ return -1
+}
+
+func SliceContains(slice []string, val string) bool {
+ for _, v := range slice {
+ if v == val {
+ return true
+ }
+ }
+ return false
+}
+
+func UniqueSet(slice []string) []string {
+ set := map[string]bool{}
+ for _, v := range slice {
+ set[v] = true
+ }
+ result := []string{}
+ for key := range set {
+ result = append(result, key)
+ }
+ return result
+}
diff --git a/qubership-apihub-service/utils/GoRoutines.go b/qubership-apihub-service/utils/GoRoutines.go
new file mode 100644
index 0000000..557cb7d
--- /dev/null
+++ b/qubership-apihub-service/utils/GoRoutines.go
@@ -0,0 +1,40 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ log "github.com/sirupsen/logrus"
+ "runtime/debug"
+)
+
+type noPanicFunc func()
+
+func (f noPanicFunc) run() {
+ defer internalRecover()
+ f()
+}
+
+func SafeAsync(function noPanicFunc) {
+ go function.run()
+}
+
+func internalRecover() {
+ if err := recover(); err != nil {
+ log.Errorf("Request failed with panic: %v", err)
+ log.Tracef("Stacktrace: %v", string(debug.Stack()))
+ debug.PrintStack()
+ return
+ }
+}
diff --git a/qubership-apihub-service/utils/HashUtils.go b/qubership-apihub-service/utils/HashUtils.go
new file mode 100644
index 0000000..9729d91
--- /dev/null
+++ b/qubership-apihub-service/utils/HashUtils.go
@@ -0,0 +1,29 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+)
+
+func GetEncodedChecksum(data ...[]byte) string {
+ allData := []byte{}
+ for _, bytes := range data {
+ allData = append(allData, bytes...)
+ }
+ sum := md5.Sum(allData)
+ return hex.EncodeToString(sum[:])
+}
diff --git a/qubership-apihub-service/utils/PGUtils.go b/qubership-apihub-service/utils/PGUtils.go
new file mode 100644
index 0000000..4d9f8ae
--- /dev/null
+++ b/qubership-apihub-service/utils/PGUtils.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import "strings"
+
+func LikeEscaped(s string) string {
+ s = strings.Replace(s, "\\", "\\\\\\\\", -1)
+ s = strings.Replace(s, "%", "\\%", -1)
+ s = strings.Replace(s, "_", "\\_", -1)
+ return s
+}
diff --git a/qubership-apihub-service/utils/PackageUtils.go b/qubership-apihub-service/utils/PackageUtils.go
new file mode 100644
index 0000000..2f33138
--- /dev/null
+++ b/qubership-apihub-service/utils/PackageUtils.go
@@ -0,0 +1,21 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import "strings"
+
+func GetPackageWorkspaceId(packageId string) string {
+ return strings.SplitN(packageId, ".", 2)[0]
+}
diff --git a/qubership-apihub-service/utils/PagingUtils.go b/qubership-apihub-service/utils/PagingUtils.go
new file mode 100644
index 0000000..259330b
--- /dev/null
+++ b/qubership-apihub-service/utils/PagingUtils.go
@@ -0,0 +1,37 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+func PaginateList(listSize int, limit int, page int) (int, int) {
+ // page count starts with 0
+ if limit < 0 || page < 0 {
+ return 0, 0
+ }
+ if limit == 0 {
+ return 0, listSize
+ }
+ startIndex := (page) * limit
+ endIndex := startIndex + limit
+
+ if startIndex >= listSize {
+ return 0, 0 // Return invalid indices if start index is out of range
+ }
+
+ if endIndex > listSize {
+ endIndex = listSize // Adjust end index to the last index if it exceeds the list size
+ }
+
+ return startIndex, endIndex
+}
diff --git a/qubership-apihub-service/utils/PagingUtils_test.go b/qubership-apihub-service/utils/PagingUtils_test.go
new file mode 100644
index 0000000..01015b4
--- /dev/null
+++ b/qubership-apihub-service/utils/PagingUtils_test.go
@@ -0,0 +1,95 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import "testing"
+
+func TestPaginateList(t *testing.T) {
+
+ startIndex, endIndex := PaginateList(100, 10, 1)
+ if startIndex != 10 || endIndex != 20 {
+ t.Errorf("Expected start index: 10, end index: 20; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(100, 10, 3)
+ if startIndex != 30 || endIndex != 40 {
+ t.Errorf("Expected start index: 30, end index: 40; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(100, 10, 10)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(0, 10, 1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, 0, 1)
+ if startIndex != 0 || endIndex != 10 {
+ t.Errorf("Expected start index: 0, end index: 10; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, 10, 0)
+ if startIndex != 0 || endIndex != 10 {
+ t.Errorf("Expected start index: 0, end index: 10; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, -10, 1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, 10, -1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(-10, 10, 1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(0, -10, -1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(-10, 0, -1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, -10, 1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(10, 10, -1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(-10, 10, 1)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+
+ startIndex, endIndex = PaginateList(0, 0, 0)
+ if startIndex != 0 || endIndex != 0 {
+ t.Errorf("Expected start index: 0, end index: 0; Got start index: %d, end index: %d", startIndex, endIndex)
+ }
+}
diff --git a/qubership-apihub-service/utils/PathUtils.go b/qubership-apihub-service/utils/PathUtils.go
new file mode 100644
index 0000000..7d22392
--- /dev/null
+++ b/qubership-apihub-service/utils/PathUtils.go
@@ -0,0 +1,68 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "path"
+ "strings"
+)
+
+// Returns normalised fileId
+func NormalizeFileId(fileId string) string {
+ // add prefix '/' since we are always operating from the root folder
+ filePath, fileName := SplitFileId("/" + fileId)
+
+ normalizedFileId := ConcatToFileId(filePath, fileName)
+ return strings.TrimPrefix(normalizedFileId, "/")
+}
+
+// Returns normalised file Path
+func NormalizeFilePath(filePath string) string {
+ // add prefix '/' since we are always operating from the root folder
+ filePath = path.Clean("/" + filePath)
+
+ if filePath == "." || filePath == "/" {
+ filePath = ""
+ }
+ return strings.TrimPrefix(filePath, "/")
+}
+
+// Splits fileId to normalized Path and Name
+func SplitFileId(fileId string) (string, string) {
+ filePath := path.Dir(fileId)
+ var fileName string
+ if strings.HasSuffix(fileId, "/") {
+ fileName = ""
+ } else {
+ fileName = path.Base(fileId)
+ }
+
+ if filePath == "." || filePath == "/" {
+ filePath = ""
+ }
+
+ return filePath, fileName
+}
+
+// Concatenates file Path and Name to fileId
+func ConcatToFileId(filePath string, fileName string) string {
+ if filePath == "" {
+ return fileName
+ } else if strings.HasSuffix(filePath, "/") {
+ return filePath + fileName
+ } else {
+ return filePath + "/" + fileName
+ }
+}
diff --git a/qubership-apihub-service/utils/PerfUtils.go b/qubership-apihub-service/utils/PerfUtils.go
new file mode 100644
index 0000000..0aace62
--- /dev/null
+++ b/qubership-apihub-service/utils/PerfUtils.go
@@ -0,0 +1,25 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import "github.com/sirupsen/logrus"
+
+func PerfLog(timeMs int64, thresholdMs int64, str string) {
+ if timeMs > thresholdMs {
+ logrus.Warnf("PERF: "+str+" took %d ms more than expected (%d ms)", timeMs, thresholdMs)
+ } else {
+ logrus.Debugf("PERF: "+str+" took %dms", timeMs)
+ }
+}
diff --git a/qubership-apihub-service/utils/UrlUtils.go b/qubership-apihub-service/utils/UrlUtils.go
new file mode 100644
index 0000000..afd3ccd
--- /dev/null
+++ b/qubership-apihub-service/utils/UrlUtils.go
@@ -0,0 +1,22 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import "net/url"
+
+func IsUrl(str string) bool {
+ u, err := url.Parse(str)
+ return err == nil && u.Scheme != "" && u.Host != ""
+}
diff --git a/qubership-apihub-service/utils/Validation.go b/qubership-apihub-service/utils/Validation.go
new file mode 100644
index 0000000..dd27127
--- /dev/null
+++ b/qubership-apihub-service/utils/Validation.go
@@ -0,0 +1,146 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package utils
+
+import (
+ "fmt"
+ "net/http"
+ "reflect"
+ "strings"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/exception"
+ "github.com/go-playground/validator/v10"
+)
+
+var validate *validator.Validate
+
+func getValidator() *validator.Validate {
+ if validate == nil {
+ validate = validator.New()
+ }
+ return validate
+}
+
+func ValidateObject(object interface{}) error {
+ err := getValidator().Struct(object)
+ if err == nil {
+ return nil
+ }
+ missingParams := make([]string, 0) //todo do not add or remove duplicate fields (e.g. arrays validations)
+ for _, err := range err.(validator.ValidationErrors) {
+ if err.Tag() == "required" {
+ missingParams = append(missingParams, err.StructNamespace())
+ }
+ }
+ if len(missingParams) == 0 {
+ return nil
+ }
+ return &exception.CustomError{
+ Status: http.StatusBadRequest,
+ Code: exception.RequiredParamsMissing,
+ Message: exception.RequiredParamsMissingMsg,
+ Params: map[string]interface{}{"params": strings.Join(getValidatedValuesTags(object, missingParams), ", ")},
+ }
+}
+
+func getValidatedValuesTags(b interface{}, targetNames []string) []string {
+ //TODO: doesn't work for documents
+ result := make([]string, 0)
+ for i := 0; i < len(targetNames); i++ {
+ reflectValue := reflect.ValueOf(b)
+ //split and remove Highest struct name
+ splitName := strings.Split(targetNames[i], ".")[1:]
+ fullErrorName := getTagReflective(reflectValue.Type(), splitName, "")
+ result = append(result, fullErrorName)
+ }
+
+ return result
+}
+
+func getTagReflective(value reflect.Type, splitName []string, name string) string {
+ currentElem, splitName := splitName[0], splitName[1:]
+ currentElemSplit := strings.Split(currentElem, "[")
+ arrayIndex := ""
+ isArr := false
+ if len(currentElemSplit) > 1 {
+ arrayIndex = strings.Split(currentElemSplit[1], "]")[0]
+ isArr = true
+ }
+ for i := 0; i < value.NumField(); i++ {
+ t := value.Field(i)
+
+ if t.Name != currentElemSplit[0] {
+ continue
+ }
+
+ if len(splitName) > 0 {
+ if name == "" {
+ name = getJsonTag(t, arrayIndex, isArr)
+ } else {
+ name = name + "." + getJsonTag(t, arrayIndex, isArr)
+ }
+
+ var nextValueType reflect.Type
+ switch os := t.Type.Kind(); os {
+ case reflect.Struct:
+ nextValueType = t.Type
+
+ case reflect.Slice:
+ nextValueType = t.Type.Elem()
+
+ case reflect.Pointer:
+ switch pointerType := t.Type.Elem().Kind(); pointerType {
+ case reflect.Struct:
+ nextValueType = t.Type.Elem()
+ case reflect.Slice:
+ nextValueType = t.Type.Elem().Elem()
+ default:
+ nextValueType = t.Type.Elem()
+ }
+ default:
+ nextValueType = t.Type
+ }
+ return getTagReflective(nextValueType, splitName, name)
+ } else {
+
+ if name == "" {
+ return getJsonTag(t, arrayIndex, isArr)
+ } else {
+ return name + "." + getJsonTag(t, arrayIndex, isArr)
+ }
+ }
+ }
+ return ""
+}
+
+func getJsonTag(field reflect.StructField, arrayIndex string, isArr bool) string {
+ jsonTag := field.Tag.Get("json")
+ fieldName := ""
+ switch jsonTag {
+ case "-", "":
+ fieldName = field.Name
+ default:
+ parts := strings.Split(jsonTag, ",")
+ fieldName = parts[0]
+ if fieldName == "" {
+ fieldName = field.Name
+ }
+ }
+ if isArr {
+ return fmt.Sprintf("%s[%s]", fieldName, arrayIndex)
+ } else {
+ return fieldName
+ }
+}
diff --git a/qubership-apihub-service/view/ActivityTracking.go b/qubership-apihub-service/view/ActivityTracking.go
new file mode 100644
index 0000000..4f52e9b
--- /dev/null
+++ b/qubership-apihub-service/view/ActivityTracking.go
@@ -0,0 +1,113 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type ActivityTrackingEvent struct {
+ Type ATEventType `json:"eventType,omitempty"`
+ Data map[string]interface{} `json:"params,omitempty"`
+ PackageId string `json:"packageId,omitempty"`
+ Date time.Time `json:"date"`
+ UserId string `json:"userId,omitempty"`
+}
+
+type PkgActivityResponseItem_depracated struct {
+ PackageName string `json:"packageName"`
+ PackageKind string `json:"kind"`
+ UserName string `json:"userName"`
+ ActivityTrackingEvent
+}
+type PkgActivityResponse_deprecated struct {
+ Events []PkgActivityResponseItem_depracated `json:"events"`
+}
+
+type PkgActivityResponseItem struct {
+ PackageName string `json:"packageName"`
+ PackageKind string `json:"kind"`
+ Principal map[string]interface{} `json:"principal,omitempty"`
+ ActivityTrackingEvent
+}
+type PkgActivityResponse struct {
+ Events []PkgActivityResponseItem `json:"events"`
+}
+
+type ActivityHistoryReq struct {
+ OnlyFavorite bool
+ TextFilter string
+ Types []string
+ Limit int
+ Page int
+ OnlyShared bool
+ Kind []string
+}
+
+type ATEventType string
+
+// access control
+
+const ATETGrantRole ATEventType = "grant_role"
+const ATETUpdateRole ATEventType = "update_role"
+const ATETDeleteRole ATEventType = "delete_role"
+
+// Apihub API keys
+
+const ATETGenerateApiKey ATEventType = "generate_api_key"
+const ATETRevokeApiKey ATEventType = "revoke_api_key"
+
+// package actions
+
+const ATETPatchPackageMeta ATEventType = "patch_package_meta"
+const ATETCreatePackage ATEventType = "create_package"
+const ATETDeletePackage ATEventType = "delete_package"
+
+// publish/versioning
+
+const ATETPublishNewVersion ATEventType = "publish_new_version"
+const ATETPublishNewRevision ATEventType = "publish_new_revision"
+const ATETPatchVersionMeta ATEventType = "patch_version_meta"
+const ATETDeleteVersion ATEventType = "delete_version"
+
+// manual groups
+
+const ATETCreateManualGroup ATEventType = "create_manual_group"
+const ATETDeleteManualGroup ATEventType = "delete_manual_group"
+const ATETOperationsGroupParameters ATEventType = "update_operations_group_parameters"
+
+func ConvertEventTypes(input []string) []string {
+ var output []string
+ for _, iType := range input {
+ switch iType {
+ case "package_members":
+ output = append(output, string(ATETGrantRole), string(ATETUpdateRole), string(ATETDeleteRole))
+ case "package_security":
+ output = append(output, string(ATETGenerateApiKey), string(ATETRevokeApiKey))
+ case "new_version":
+ output = append(output, string(ATETPublishNewVersion))
+ case "package_version":
+ output = append(output, string(ATETPublishNewRevision), string(ATETPatchVersionMeta), string(ATETDeleteVersion))
+ case "package_management":
+ output = append(output, string(ATETPatchPackageMeta), string(ATETCreatePackage), string(ATETDeletePackage))
+ case "operations_group":
+ output = append(output, string(ATETCreateManualGroup), string(ATETDeleteManualGroup), string(ATETOperationsGroupParameters))
+ }
+ }
+ return output
+}
+
+type EventRoleView struct {
+ RoleId string `json:"roleId"`
+ Role string `json:"role"`
+}
diff --git a/qubership-apihub-service/view/Admin.go b/qubership-apihub-service/view/Admin.go
new file mode 100644
index 0000000..5f91636
--- /dev/null
+++ b/qubership-apihub-service/view/Admin.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Admins struct {
+ Admins []User `json:"admins"`
+}
+
+type AddSysadmReq struct {
+ UserId string `json:"userId" validate:"required"`
+}
diff --git a/qubership-apihub-service/view/Agent.go b/qubership-apihub-service/view/Agent.go
new file mode 100644
index 0000000..ad1a11a
--- /dev/null
+++ b/qubership-apihub-service/view/Agent.go
@@ -0,0 +1,70 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "strings"
+ "time"
+)
+
+type AgentKeepaliveMessage struct {
+ Cloud string `json:"cloud" validate:"required"`
+ Namespace string `json:"namespace" validate:"required"`
+ Url string `json:"url" validate:"required"`
+ BackendVersion string `json:"backendVersion" validate:"required"`
+ Name string `json:"name"`
+ AgentVersion string `json:"agentVersion"`
+}
+
+type AgentStatus string
+
+const AgentStatusActive AgentStatus = "active"
+const AgentStatusInactive AgentStatus = "inactive"
+
+type AgentInstance struct {
+ AgentId string `json:"agentId"`
+ AgentDeploymentCloud string `json:"agentDeploymentCloud"`
+ AgentDeploymentNamespace string `json:"agentDeploymentNamespace"`
+ AgentUrl string `json:"agentUrl"`
+ LastActive time.Time `json:"lastActive"`
+ Status AgentStatus `json:"status"`
+ BackendVersion string `json:"backendVersion"`
+ Name string `json:"name"`
+ AgentVersion string `json:"agentVersion"`
+ CompatibilityError *AgentCompatibilityError `json:"compatibilityError,omitempty"`
+}
+
+func MakeAgentId(cloud, namespace string) string {
+ return strings.ToLower(cloud) + "_" + strings.ToLower(namespace)
+}
+
+type AgentNamespaces struct {
+ Namespaces []string `json:"namespaces"`
+ CloudName string `json:"cloudName"`
+}
+
+type AgentVersion struct {
+ Version string `json:"version"`
+}
+
+type AgentCompatibilityError struct {
+ Severity AgentCompatibilityErrorSeverity `json:"severity"`
+ Message string `json:"message"`
+}
+
+type AgentCompatibilityErrorSeverity string
+
+const SeverityError AgentCompatibilityErrorSeverity = "error"
+const SeverityWarning AgentCompatibilityErrorSeverity = "warning"
diff --git a/qubership-apihub-service/view/ApiConfig.go b/qubership-apihub-service/view/ApiConfig.go
new file mode 100644
index 0000000..15dca37
--- /dev/null
+++ b/qubership-apihub-service/view/ApiConfig.go
@@ -0,0 +1,25 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ApiConfig struct {
+ ConfigUrl string `json:"configUrl"`
+ Urls []Url `json:"urls"`
+}
+
+type Url struct {
+ Url string `json:"url"`
+ Name string `json:"name"`
+}
diff --git a/qubership-apihub-service/view/ApiKey.go b/qubership-apihub-service/view/ApiKey.go
new file mode 100644
index 0000000..afde728
--- /dev/null
+++ b/qubership-apihub-service/view/ApiKey.go
@@ -0,0 +1,28 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ApiKeyStatus struct {
+ Status string `json:"status"`
+}
+
+type ApiKeyRequest struct {
+ ApiKey string `json:"apikey"`
+}
+
+type ApiKey struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+}
diff --git a/qubership-apihub-service/view/ApihubApiKey.go b/qubership-apihub-service/view/ApihubApiKey.go
new file mode 100644
index 0000000..e806e9e
--- /dev/null
+++ b/qubership-apihub-service/view/ApihubApiKey.go
@@ -0,0 +1,87 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "time"
+)
+
+type ApihubApiKey_deprecated struct {
+ Id string `json:"id"`
+ PackageId string `json:"packageId"`
+ Name string `json:"name"`
+ CreatedBy string `json:"createdBy"`
+ CreatedAt time.Time `json:"createdAt"`
+ DeletedBy string `json:"deletedBy,omitempty"`
+ DeletedAt *time.Time `json:"deletedAt,omitempty"`
+ ApiKey string `json:"apiKey,omitempty"`
+ Roles []string `json:"roles"`
+}
+
+type ApihubApiKeys_deprecated struct {
+ ApiKeys []ApihubApiKey_deprecated `json:"apiKeys"`
+}
+
+type ApihubApiKey_v3_deprecated struct {
+ Id string `json:"id"`
+ PackageId string `json:"packageId"`
+ Name string `json:"name"`
+ CreatedBy User `json:"createdBy"`
+ CreatedAt time.Time `json:"createdAt"`
+ DeletedBy string `json:"deletedBy,omitempty"`
+ DeletedAt *time.Time `json:"deletedAt,omitempty"`
+ ApiKey string `json:"apiKey,omitempty"`
+ Roles []string `json:"roles"`
+}
+
+type ApihubApiKeys_v3_deprecated struct {
+ ApiKeys []ApihubApiKey_v3_deprecated `json:"apiKeys"`
+}
+
+type ApihubApiKey struct {
+ Id string `json:"id"`
+ PackageId string `json:"packageId"`
+ Name string `json:"name"`
+ CreatedBy User `json:"createdBy"`
+ CreatedFor *User `json:"createdFor,omitempty"`
+ CreatedAt time.Time `json:"createdAt"`
+ DeletedBy string `json:"deletedBy,omitempty"`
+ DeletedAt *time.Time `json:"deletedAt,omitempty"`
+ ApiKey string `json:"apiKey,omitempty"`
+ Roles []string `json:"roles"`
+}
+
+type ApihubApiKeys struct {
+ ApiKeys []ApihubApiKey `json:"apiKeys"`
+}
+
+type ApihubApiKeyCreateReq_deprecated struct {
+ Name string `json:"name" validate:"required"`
+ Roles []string `json:"roles"`
+}
+
+type ApihubApiKeyCreateReq struct {
+ Name string `json:"name" validate:"required"`
+ CreatedFor string `json:"createdFor"`
+ Roles []string `json:"roles"`
+}
+
+type ApihubApiKeyExtAuthView struct {
+ Id string `json:"id"`
+ PackageId string `json:"packageId"`
+ Name string `json:"name"`
+ Revoked bool `json:"revoked"`
+ Roles []string `json:"roles"`
+}
diff --git a/qubership-apihub-service/view/Audience.go b/qubership-apihub-service/view/Audience.go
new file mode 100644
index 0000000..93adde2
--- /dev/null
+++ b/qubership-apihub-service/view/Audience.go
@@ -0,0 +1,27 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const ApiAudienceInternal = "internal"
+const ApiAudienceExternal = "external"
+const ApiAudienceUnknown = "unknown"
+
+func ValidApiAudience(apiAudience string) bool {
+ switch apiAudience {
+ case ApiAudienceInternal, ApiAudienceExternal, ApiAudienceUnknown:
+ return true
+ }
+ return false
+}
diff --git a/qubership-apihub-service/view/Branch.go b/qubership-apihub-service/view/Branch.go
new file mode 100644
index 0000000..178143c
--- /dev/null
+++ b/qubership-apihub-service/view/Branch.go
@@ -0,0 +1,89 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Branch struct {
+ ProjectId string `json:"projectId" msgpack:"projectId"`
+ Editors []User `json:"editors" msgpack:"editors"`
+ ConfigFileId string `json:"configFileId,omitempty" msgpack:"configFileId,omitempty"`
+ ChangeType ChangeType `json:"changeType,omitempty" msgpack:"changeType,omitempty"`
+ Permissions *[]string `json:"permissions,omitempty" msgpack:"permissions,omitempty"` //show only when permissions are calculated
+ Files []Content `json:"files" validate:"dive,required" msgpack:"files"`
+ Refs []Ref `json:"refs" msgpack:"refs"`
+}
+
+type BranchGitConfigView struct {
+ ProjectId string `json:"projectId"`
+ Files []ContentGitConfigView `json:"files"`
+ Refs []RefGitConfigView `json:"refs"`
+}
+
+type Branches struct {
+ Branches []Branch `json:"branches"`
+}
+
+func (b *Branch) RemoveFolders() {
+ onlyFiles := make([]Content, 0)
+ for _, content := range b.Files {
+ if !content.IsFolder {
+ onlyFiles = append(onlyFiles, content)
+ }
+ }
+ b.Files = onlyFiles
+}
+
+func TransformBranchToGitView(branch Branch) *BranchGitConfigView {
+ resFiles := make([]ContentGitConfigView, 0)
+ resRefs := make([]RefGitConfigView, 0)
+
+ for _, f := range branch.Files {
+ if f.FromFolder {
+ continue
+ }
+ if f.IsFolder {
+ f.Publish = false
+ f.Labels = []string{}
+ }
+ resFiles = append(resFiles, TransformContentToGitView(f))
+ }
+
+ for _, r := range branch.Refs {
+ resRefs = append(resRefs, TransformRefToGitView(r))
+ }
+
+ return &BranchGitConfigView{
+ ProjectId: branch.ProjectId,
+ Files: resFiles,
+ Refs: resRefs,
+ }
+}
+
+func TransformGitToBranchView(branch *BranchGitConfigView, refs []Ref) *Branch {
+
+ resContent := make([]Content, 0)
+ for _, file := range branch.Files {
+ resContent = append(resContent, TransformGitViewToContent(file))
+ }
+
+ if refs == nil {
+ refs = make([]Ref, 0)
+ }
+
+ return &Branch{
+ ProjectId: branch.ProjectId,
+ Files: resContent,
+ Refs: refs,
+ }
+}
diff --git a/qubership-apihub-service/view/BranchChanges.go b/qubership-apihub-service/view/BranchChanges.go
new file mode 100644
index 0000000..2a83a42
--- /dev/null
+++ b/qubership-apihub-service/view/BranchChanges.go
@@ -0,0 +1,22 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ChangeType string
+
+const CTAdded ChangeType = "added"
+const CTUpdated ChangeType = "updated"
+const CTDeleted ChangeType = "deleted"
+const CTUnchanged ChangeType = "none"
diff --git a/qubership-apihub-service/view/BranchConflicts.go b/qubership-apihub-service/view/BranchConflicts.go
new file mode 100644
index 0000000..a5ffe51
--- /dev/null
+++ b/qubership-apihub-service/view/BranchConflicts.go
@@ -0,0 +1,25 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type BranchConflicts struct {
+ Files []string `json:"files"`
+}
+
+type FileConflict struct {
+ FileId string
+ ConflictedBlobId string
+ ConflictedFileId *string
+}
diff --git a/qubership-apihub-service/view/Build.go b/qubership-apihub-service/view/Build.go
new file mode 100644
index 0000000..00273a9
--- /dev/null
+++ b/qubership-apihub-service/view/Build.go
@@ -0,0 +1,240 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/exception"
+)
+
+type BuildConfig struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ BuildType string `json:"buildType"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Status string `json:"status"`
+ Refs []BCRef `json:"refs,omitempty"`
+ Files []BCFile `json:"files,omitempty"`
+ PublishId string `json:"publishId"`
+ Metadata BuildConfigMetadata `json:"metadata,omitempty"`
+ CreatedBy string `json:"createdBy"`
+ NoChangelog bool `json:"noChangeLog,omitempty"` // for migration
+ PublishedAt time.Time `json:"publishedAt,omitempty"` // for migration
+ MigrationBuild bool `json:"migrationBuild,omitempty"` //for migration
+ MigrationId string `json:"migrationId,omitempty"` //for migration
+ ComparisonRevision int `json:"comparisonRevision,omitempty"`
+ ComparisonPrevRevision int `json:"comparisonPrevRevision,omitempty"`
+ UnresolvedRefs bool `json:"unresolvedRefs,omitempty"`
+ ResolveRefs bool `json:"resolveRefs,omitempty"`
+ ResolveConflicts bool `json:"resolveConflicts,omitempty"`
+ ServiceName string `json:"serviceName,omitempty"`
+ ApiType string `json:"apiType,omitempty"` //for operation group
+ GroupName string `json:"groupName,omitempty"` //for operation group
+ Format string `json:"format,omitempty"` //for operation group
+ ExternalMetadata map[string]interface{} `json:"externalMetadata,omitempty"`
+}
+
+type BuildConfigMetadata struct {
+ BranchName string `json:"branchName,omitempty"`
+ RepositoryUrl string `json:"repositoryUrl,omitempty"`
+ CloudName string `json:"cloudName,omitempty"`
+ CloudUrl string `json:"cloudUrl,omitempty"`
+ Namespace string `json:"namespace,omitempty"`
+ VersionLabels []string `json:"versionLabels,omitempty"`
+}
+
+type BCRef struct {
+ RefId string `json:"refId"`
+ Version string `json:"version"` //format: version@revision
+ ParentRefId string `json:"parentRefId"`
+ ParentVersion string `json:"parentVersion"` //format: version@revision
+ Excluded bool `json:"excluded,omitempty"`
+}
+
+type BCFile struct {
+ FileId string `json:"fileId"`
+ Slug string `json:"slug"` //for migration
+ Index int `json:"index"` //for migration
+ Publish *bool `json:"publish"`
+ Labels []string `json:"labels"`
+ BlobId string `json:"blobId,omitempty"`
+ XApiKind string `json:"xApiKind,omitempty"`
+}
+
+type BuildStatusEnum string
+
+const StatusNotStarted BuildStatusEnum = "none"
+const StatusRunning BuildStatusEnum = "running"
+const StatusComplete BuildStatusEnum = "complete"
+const StatusError BuildStatusEnum = "error"
+
+// todo string -> BuildType type
+const ChangelogType string = "changelog"
+const BuildType string = "build"
+const DocumentGroupType_deprecated string = "documentGroup"
+const ReducedSourceSpecificationsType string = "reducedSourceSpecifications"
+const MergedSpecificationType string = "mergedSpecification"
+
+func ValidateGroupBuildType(buildType string) error {
+ switch buildType {
+ case ReducedSourceSpecificationsType, MergedSpecificationType:
+ return nil
+ }
+ return &exception.CustomError{
+ Status: http.StatusBadRequest,
+ Code: exception.InvalidParameterValue,
+ Message: exception.InvalidParameterValueMsg,
+ Params: map[string]interface{}{"param": "buildType", "value": buildType},
+ }
+}
+
+func BuildStatusFromString(str string) (BuildStatusEnum, error) {
+ switch str {
+ case "none":
+ return StatusNotStarted, nil
+ case "running":
+ return StatusRunning, nil
+ case "complete":
+ return StatusComplete, nil
+ case "error":
+ return StatusError, nil
+ }
+ return StatusNotStarted, fmt.Errorf("unknown build status: %s", str)
+}
+
+func BuildConfigToMap(bc BuildConfig) (*map[string]interface{}, error) {
+ var confAsMap map[string]interface{}
+ cBytes, err := json.Marshal(bc)
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(cBytes, &confAsMap)
+ if err != nil {
+ return nil, err
+ }
+ return &confAsMap, nil
+}
+
+func BuildConfigFromMap(confAsMap map[string]interface{}, publishId string) (*BuildConfig, error) {
+ var bc BuildConfig
+ cBytes, err := json.Marshal(confAsMap)
+ if err != nil {
+ return nil, err
+ }
+ err = json.Unmarshal(cBytes, &bc)
+ if err != nil {
+ return nil, err
+ }
+ bc.PublishId = publishId
+ return &bc, nil
+}
+
+func BcRefsToRefs(refs []BCRef) []Ref {
+ var result []Ref
+ for _, ref := range refs {
+ result = append(result, BCRefToRef(ref))
+ }
+ return result
+}
+
+func BCRefToRef(r BCRef) Ref {
+ return Ref{
+ RefPackageId: r.RefId,
+ RefPackageName: "",
+ RefPackageVersion: r.Version,
+ Status: "",
+ VersionStatus: "",
+ Kind: "",
+ }
+}
+
+type PublishStatusResponse struct {
+ PublishId string `json:"publishId"`
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
+
+func IsBuildFinished(status BuildStatusEnum) bool {
+ if status == StatusComplete || status == StatusError {
+ return true
+ }
+ return false
+}
+
+type BuildsStatusRequest struct {
+ PublishIds []string `json:"publishIds"`
+}
+
+type ChangelogBuildSearchRequest struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ PreviousVersion string `json:"previousVersion"`
+ BuildType string `json:"buildType"`
+ ComparisonRevision int `json:"comparisonRevision"`
+ ComparisonPrevRevision int `json:"comparisonPrevRevision"`
+}
+
+type DocumentGroupBuildSearchRequest struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ BuildType string `json:"buildType"`
+ Format string `json:"format"`
+ ApiType string `json:"apiType"`
+ GroupName string `json:"groupName"`
+}
+
+type BuildView struct {
+ BuildId string `json:"buildId,omitempty"`
+ Status string `json:"status"`
+ Details string `json:"details"`
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ CreatedAt time.Time `json:"createdAt"`
+ LastActive time.Time `json:"lastActive"`
+ CreatedBy string `json:"createdBy,omitempty"`
+ RestartCount int `json:"restart_count"`
+}
+
+type PublishedVersionSourceDataConfig struct {
+ Sources []byte `json:"sources"`
+ Config BuildConfig `json:"config"`
+}
+
+type ChangelogBuildConfigView struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ BuildType string `json:"buildType"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ CreatedBy string `json:"createdBy"`
+ BuildId string `json:"buildId"`
+}
+
+type DocumentTransformConfigView struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ BuildType string `json:"buildType"`
+ Format string `json:"format,omitempty"`
+ ApiType string `json:"apiType"`
+ GroupName string `json:"groupName"`
+ CreatedBy string `json:"createdBy"`
+ BuildId string `json:"buildId"`
+}
diff --git a/qubership-apihub-service/view/BusinessMetric.go b/qubership-apihub-service/view/BusinessMetric.go
new file mode 100644
index 0000000..a8c49ac
--- /dev/null
+++ b/qubership-apihub-service/view/BusinessMetric.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type BusinessMetric struct {
+ Date string `json:"date"`
+ PackageId string `json:"packageId"`
+ Metric string `json:"metric"`
+ Username string `json:"username"`
+ Value int `json:"value"`
+}
diff --git a/qubership-apihub-service/view/CommonOperation.go b/qubership-apihub-service/view/CommonOperation.go
new file mode 100644
index 0000000..fad6bc3
--- /dev/null
+++ b/qubership-apihub-service/view/CommonOperation.go
@@ -0,0 +1,549 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "fmt"
+
+ "github.com/iancoleman/orderedmap"
+)
+
+type Operation struct {
+ OperationId string `json:"operationId" validate:"required"`
+ Title string `json:"title" validate:"required"`
+ ApiType string `json:"apiType" validate:"required"`
+ DataHash string `json:"dataHash" validate:"required"`
+ Deprecated bool `json:"deprecated"`
+ ApiKind string `json:"apiKind" validate:"required"`
+ Metadata map[string]interface{} `json:"metadata" validate:"required"`
+ SearchScopes map[string]interface{} `json:"searchScopes" validate:"required"`
+ PreviousReleaseVersions []string `json:"deprecatedInPreviousVersions"`
+ DeprecatedInfo string `json:"deprecatedInfo"`
+ DeprecatedItems []DeprecatedItem `json:"deprecatedItems"`
+ Tags []string `json:"tags"`
+ Models map[string]string `json:"models"`
+ ApiAudience string `json:"apiAudience" validate:"required"`
+}
+
+type DocumentsOperation_deprecated struct {
+ OperationId string `json:"operationId" validate:"required"`
+ Title string `json:"title" validate:"required"`
+ ApiType string `json:"apiType" validate:"required"`
+ DataHash string `json:"dataHash" validate:"required"`
+ Deprecated bool `json:"deprecated"`
+ ApiKind string `json:"apiKind" validate:"required"`
+ Metadata interface{} `json:"metadata" validate:"required"`
+}
+
+type SingleOperationView struct {
+ Data *orderedmap.OrderedMap `json:"data,omitempty"`
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ DataHash string `json:"dataHash"`
+ Deprecated bool `json:"deprecated,omitempty"`
+ ApiKind string `json:"apiKind"`
+ ApiType string `json:"apiType"`
+ CustomTags map[string]interface{} `json:"customTags,omitempty"`
+ ApiAudience string `json:"apiAudience"`
+}
+
+type CommonOperationView struct {
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ DataHash string `json:"dataHash"`
+ Deprecated bool `json:"deprecated,omitempty"`
+ ApiKind string `json:"apiKind"`
+ ApiType string `json:"apiType"`
+ CustomTags map[string]interface{} `json:"customTags,omitempty"`
+ ApiAudience string `json:"apiAudience"`
+}
+
+type OperationListView struct {
+ CommonOperationView
+ PackageRef string `json:"packageRef,omitempty"`
+ Data *orderedmap.OrderedMap `json:"data,omitempty"`
+}
+
+type DeprecatedOperationView struct {
+ PackageRef string `json:"packageRef,omitempty"`
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ DataHash string `json:"dataHash"`
+ Deprecated bool `json:"deprecated,omitempty"`
+ ApiKind string `json:"apiKind"`
+ ApiType string `json:"apiType"`
+ PreviousReleaseVersions []string `json:"deprecatedInPreviousVersions,omitempty"`
+ DeprecatedCount int `json:"deprecatedCount"`
+ DeprecatedInfo string `json:"deprecatedInfo,omitempty"`
+ DeprecatedItems []DeprecatedItem `json:"deprecatedItems,omitempty"`
+ ApiAudience string `json:"apiAudience"`
+}
+type DeprecatedItem struct {
+ PreviousReleaseVersions []string `json:"deprecatedInPreviousVersions,omitempty"`
+ DeclarationJsonPaths [][]interface{} `json:"declarationJsonPaths,omitempty"`
+ Description string `json:"description,omitempty"`
+ Hash string `json:"hash,omitempty"`
+ TolerantHash string `json:"tolerantHash,omitempty"`
+ DeprecatedInfo string `json:"deprecatedInfo,omitempty"`
+}
+
+type DeprecatedItems struct {
+ DeprecatedItems []DeprecatedItem `json:"deprecatedItems"`
+}
+
+type OperationComparison struct {
+ OperationId string `json:"operationId" validate:"required"`
+ DataHash string `json:"dataHash,omitempty"`
+ PreviousDataHash string `json:"previousDataHash,omitempty"`
+ ChangeSummary ChangeSummary `json:"changeSummary,omitempty"`
+ Changes []interface{} `json:"changes" validate:"required,dive,required"`
+ JsonPath []string `json:"jsonPath,omitempty"`
+ Action string `json:"action,omitempty"`
+ Severity string `json:"severity,omitempty"`
+ Metadata map[string]interface{} `json:"metadata"`
+}
+
+type SingleOperationChangeAdd struct {
+ SingleOperationChangeCommon
+ CurrentDeclarationJsonPaths [][]interface{} `json:"currentDeclarationJsonPaths,omitempty"`
+ CurrentValueHash string `json:"currentValueHash,omitempty"`
+}
+
+type SingleOperationChangeRemove struct {
+ SingleOperationChangeCommon
+ PreviousDeclarationJsonPaths [][]interface{} `json:"previousDeclarationJsonPaths,omitempty"`
+ PreviousValueHash string `json:"previousValueHash,omitempty"`
+}
+
+type SingleOperationChangeReplace struct {
+ SingleOperationChangeCommon
+ CurrentDeclarationJsonPaths [][]interface{} `json:"currentDeclarationJsonPaths,omitempty"`
+ CurrentValueHash string `json:"currentValueHash,omitempty"`
+ PreviousDeclarationJsonPaths [][]interface{} `json:"previousDeclarationJsonPaths,omitempty"`
+ PreviousValueHash string `json:"previousValueHash,omitempty"`
+}
+
+type SingleOperationChangeRename struct {
+ SingleOperationChangeCommon
+ CurrentDeclarationJsonPaths [][]interface{} `json:"currentDeclarationJsonPaths,omitempty"`
+ CurrentKey string `json:"currentKey,omitempty"`
+ PreviousDeclarationJsonPaths [][]interface{} `json:"previousDeclarationJsonPaths,omitempty"`
+ PreviousKey string `json:"previousKey,omitempty"`
+}
+
+type SingleOperationChangeCommon struct {
+ Action string `json:"action,omitempty"`
+ Severity string `json:"severity,omitempty"`
+ Description string `json:"description,omitempty"`
+ Scope string `json:"scope,omitempty"`
+}
+
+func GetSingleOperationChangeCommon(change interface{}) SingleOperationChangeCommon {
+ if change != nil {
+ if val, ok := change.(SingleOperationChangeAdd); ok {
+ return val.SingleOperationChangeCommon
+ }
+ if val, ok := change.(SingleOperationChangeRemove); ok {
+ return val.SingleOperationChangeCommon
+ }
+ if val, ok := change.(SingleOperationChangeReplace); ok {
+ return val.SingleOperationChangeCommon
+ }
+ if val, ok := change.(SingleOperationChangeRename); ok {
+ return val.SingleOperationChangeCommon
+ }
+ if val, ok := change.(SingleOperationChangeCommon); ok {
+ return val
+ }
+ }
+ return SingleOperationChangeCommon{}
+}
+
+func ParseSingleOperationChange(change interface{}) interface{} {
+ if change == nil {
+ return SingleOperationChangeCommon{}
+ }
+ var currentDeclarationJsonPaths [][]interface{}
+ var previousDeclarationJsonPaths [][]interface{}
+ var currentValueHash string
+ var previousValueHash string
+ var currentKey string
+ var previousKey string
+ singleOperationChangeCommon := SingleOperationChangeCommon{}
+ if change, ok := change.(map[string]interface{}); ok {
+ if val, ok := change["description"].(string); ok {
+ singleOperationChangeCommon.Description = val
+ }
+ if val, ok := change["severity"].(string); ok {
+ singleOperationChangeCommon.Severity = val
+ }
+ if val, ok := change["scope"].(string); ok {
+ singleOperationChangeCommon.Scope = val
+ }
+ if singleOperationChangeCommon.Action, ok = change["action"].(string); !ok {
+ return singleOperationChangeCommon
+ }
+ if currentJsonPathArr, ok := change["currentDeclarationJsonPaths"].([]interface{}); ok {
+ for _, currentJsonPath := range currentJsonPathArr {
+ objPathArr := make([]interface{}, 0)
+ if pathArr, ok := currentJsonPath.([]interface{}); ok {
+ objPathArr = append(objPathArr, pathArr...)
+ }
+ if len(objPathArr) > 0 {
+ currentDeclarationJsonPaths = append(currentDeclarationJsonPaths, objPathArr)
+ }
+ }
+ }
+ if previousJsonPathArr, ok := change["previousDeclarationJsonPaths"].([]interface{}); ok {
+ for _, previousJsonPath := range previousJsonPathArr {
+ objPathArr := make([]interface{}, 0)
+ if pathArr, ok := previousJsonPath.([]interface{}); ok {
+ objPathArr = append(objPathArr, pathArr...)
+ }
+ if len(objPathArr) > 0 {
+ previousDeclarationJsonPaths = append(previousDeclarationJsonPaths, objPathArr)
+ }
+ }
+ }
+ if val, ok := change["currentValueHash"].(string); ok {
+ currentValueHash = val
+ }
+ if val, ok := change["previousValueHash"].(string); ok {
+ previousValueHash = val
+ }
+ if val, ok := change["currentKey"].(string); ok {
+ currentKey = val
+ }
+ if val, ok := change["previousKey"].(string); ok {
+ previousKey = val
+ }
+
+ switch singleOperationChangeCommon.Action {
+ case "add":
+ return SingleOperationChangeAdd{
+ SingleOperationChangeCommon: singleOperationChangeCommon,
+ CurrentDeclarationJsonPaths: currentDeclarationJsonPaths,
+ CurrentValueHash: currentValueHash,
+ }
+ case "remove":
+ return SingleOperationChangeRemove{
+ SingleOperationChangeCommon: singleOperationChangeCommon,
+ PreviousDeclarationJsonPaths: previousDeclarationJsonPaths,
+ PreviousValueHash: previousValueHash,
+ }
+ case "replace":
+ return SingleOperationChangeReplace{
+ SingleOperationChangeCommon: singleOperationChangeCommon,
+ CurrentDeclarationJsonPaths: currentDeclarationJsonPaths,
+ CurrentValueHash: currentValueHash,
+ PreviousDeclarationJsonPaths: previousDeclarationJsonPaths,
+ PreviousValueHash: previousValueHash,
+ }
+ case "rename":
+ return SingleOperationChangeRename{
+ SingleOperationChangeCommon: singleOperationChangeCommon,
+ CurrentDeclarationJsonPaths: currentDeclarationJsonPaths,
+ CurrentKey: currentKey,
+ PreviousDeclarationJsonPaths: previousDeclarationJsonPaths,
+ PreviousKey: previousKey,
+ }
+ }
+ }
+ return singleOperationChangeCommon
+}
+
+type VersionChangesView struct {
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Operations []interface{} `json:"operations"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+type OperationComparisonChangelogView_deprecated struct {
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ ApiKind string `json:"apiKind,omitempty"`
+ DataHash string `json:"dataHash,omitempty"`
+ PreviousDataHash string `json:"previousDataHash,omitempty"`
+ ChangeSummary ChangeSummary `json:"changeSummary"`
+ PackageRef string `json:"packageRef"`
+ PreviousVersionPackageRef string `json:"previousVersionPackageRef"`
+}
+
+type ComparisonOperationView struct {
+ Title string `json:"title"`
+ ApiKind string `json:"apiKind,omitempty"`
+ ApiAudience string `json:"apiAudience"`
+ DataHash string `json:"dataHash,omitempty"`
+ PackageRef string `json:"packageRef"`
+}
+
+type OperationComparisonChangelogView struct {
+ OperationId string `json:"operationId"`
+ CurrentOperation *ComparisonOperationView `json:"currentOperation,omitempty"`
+ PreviousOperation *ComparisonOperationView `json:"previousOperation,omitempty"`
+ ChangeSummary ChangeSummary `json:"changeSummary"`
+}
+
+type OperationComparisonChangesView struct {
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ ApiKind string `json:"apiKind,omitempty"`
+ DataHash string `json:"dataHash,omitempty"`
+ PreviousDataHash string `json:"previousDataHash,omitempty"`
+ ChangeSummary ChangeSummary `json:"changeSummary"`
+ PackageRef string `json:"packageRef"`
+ PreviousVersionPackageRef string `json:"previousVersionPackageRef"`
+ Changes []interface{} `json:"changes"`
+ Action string `json:"action"`
+}
+
+type OperationChangesView struct {
+ Changes []interface{} `json:"changes"`
+}
+
+type OperationTags struct {
+ Tags []string `json:"tags"`
+}
+
+type Operations struct {
+ Operations []interface{} `json:"operations"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+type GroupedOperations struct {
+ Operations []interface{} `json:"operations"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+type GroupedGhostOperations_deprecated struct {
+ GhostOperations []interface{} `json:"ghostOperations"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+type ChangeSummary struct {
+ Breaking int `json:"breaking"`
+ SemiBreaking int `json:"semi-breaking"`
+ Deprecated int `json:"deprecated"`
+ NonBreaking int `json:"non-breaking"`
+ Annotation int `json:"annotation"`
+ Unclassified int `json:"unclassified"`
+}
+
+func (c ChangeSummary) GetTotalSummary() int {
+ return c.Breaking + c.SemiBreaking + c.Deprecated + c.NonBreaking + c.Annotation + c.Unclassified
+}
+
+const ChangelogActionChange string = "change"
+const ChangelogActionAdd string = "add"
+const ChangelogActionRemove string = "remove"
+
+type ApiKind string
+
+const BwcApiKind ApiKind = "bwc"
+const NoBwcApiKind ApiKind = "no-bwc"
+const DebugApiKind ApiKind = "debug"
+const ExperimentalApiKind ApiKind = "experimental"
+
+type Severity string
+
+const Annotation Severity = "annotation"
+const Breaking Severity = "breaking"
+const SemiBreaking Severity = "semi-breaking"
+const Deprecated Severity = "deprecated"
+const NonBreaking Severity = "non-breaking"
+const Unclassified Severity = "unclassified"
+
+func ValidSeverity(s string) bool {
+ switch s {
+ case string(Annotation), string(Breaking), string(SemiBreaking), string(Deprecated), string(NonBreaking), string(Unclassified):
+ return true
+ }
+ return false
+}
+
+func ParseApiKind(s string) (ApiKind, error) {
+ switch s {
+ case string(BwcApiKind):
+ return BwcApiKind, nil
+ case string(NoBwcApiKind):
+ return NoBwcApiKind, nil
+ case string(DebugApiKind):
+ return DebugApiKind, nil
+ case string(ExperimentalApiKind):
+ return ExperimentalApiKind, nil
+ default:
+ return "", fmt.Errorf("unknown API Kind: %v", s)
+ }
+}
+
+type ApiType string
+
+const RestApiType ApiType = "rest"
+const GraphqlApiType ApiType = "graphql"
+const ProtobufApiType ApiType = "protobuf"
+
+func ParseApiType(s string) (ApiType, error) {
+ switch s {
+ case string(RestApiType):
+ return RestApiType, nil
+ case string(GraphqlApiType):
+ return GraphqlApiType, nil
+ case string(ProtobufApiType):
+ return ProtobufApiType, nil
+ default:
+ return "", fmt.Errorf("unknown API Type: %v", s)
+ }
+}
+
+func GetDocumentTypesForApiType(apiType string) []string {
+ switch apiType {
+ case string(RestApiType):
+ return []string{OpenAPI20Type, OpenAPI30Type, OpenAPI31Type}
+ case string(GraphqlApiType):
+ return []string{GraphQLSchemaType, GraphAPIType, IntrospectionType}
+ case string(ProtobufApiType):
+ return []string{Protobuf3Type}
+ default:
+ return []string{}
+ }
+}
+
+type OperationListReq struct {
+ Deprecated *bool
+ HashList []string
+ Ids []string
+ IncludeData bool
+ Kind string
+ EmptyTag bool
+ Tag string
+ Limit int
+ Page int
+ TextFilter string
+ ApiType string
+ DocumentSlug string
+ EmptyGroup bool
+ Group string
+ OnlyAddable bool
+ RefPackageId string
+ CustomTagKey string
+ CustomTagValue string
+ ApiAudience string
+}
+
+type DeprecatedOperationListReq struct {
+ Ids []string
+ Kind string
+ Tags []string
+ Limit int
+ Page int
+ TextFilter string
+ ApiType string
+ DocumentSlug string
+ IncludeDeprecatedItems bool
+ RefPackageId string
+ EmptyTag bool
+ EmptyGroup bool
+ Group string
+ ApiAudience string
+}
+
+type OperationBasicSearchReq struct {
+ PackageId string
+ Version string
+ OperationId string
+ Revision int
+ ApiType string
+ ApiKind string
+ Limit int
+ Offset int
+ TextFilter string
+ ApiAudience string
+}
+
+type VersionChangesReq struct {
+ PreviousVersion string
+ PreviousVersionPackageId string
+ DocumentSlug string
+ ApiKind string
+ EmptyTag bool
+ RefPackageId string
+ Tags []string
+ TextFilter string
+ Limit int
+ Offset int
+ EmptyGroup bool
+ Group string
+ Severities []string
+ ApiAudience string
+}
+
+type PagingFilterReq struct {
+ Limit int
+ Offset int
+ TextFilter string
+}
+
+type DocumentsFilterReq struct {
+ ApiType string
+ Limit int
+ Offset int
+ TextFilter string
+}
+
+type DocumentsForTransformationFilterReq struct {
+ ApiType string
+ Limit int
+ Offset int
+ FilterByOperationGroup string
+}
+type ChangelogCalculationParams struct {
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ ReCalculate bool `json:"reCalculate"`
+}
+
+type CalculationProcessStatus struct {
+ Status string `json:"status,omitempty"`
+ Message string `json:"message"`
+}
+
+type DeprecatedOperationsSummary struct {
+ OperationTypes *[]DeprecatedOperationType `json:"operationTypes,omitempty"`
+ Refs *[]DeprecatedOperationTypesRef `json:"refs,omitempty"`
+ Packages *map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+type DeprecatedOperationType struct {
+ ApiType string `json:"apiType"`
+ DeprecatedCount int `json:"deprecatedCount"`
+ Tags []string `json:"tags"`
+}
+type DeprecatedOperationTypesRef struct {
+ PackageRef string `json:"packageRef,omitempty"`
+ OperationTypes []DeprecatedOperationType `json:"operationTypes"`
+}
+
+type OperationModelUsages struct {
+ ModelUsages []OperationModels `json:"modelUsages"`
+}
+
+type OperationModels struct {
+ OperationId string `json:"operationId"`
+ ModelNames []string `json:"modelNames"`
+}
+
+type CustomTags struct {
+ CustomTag map[string]interface{}
+}
diff --git a/qubership-apihub-service/view/Comparison.go b/qubership-apihub-service/view/Comparison.go
new file mode 100644
index 0000000..e4e3660
--- /dev/null
+++ b/qubership-apihub-service/view/Comparison.go
@@ -0,0 +1,29 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type VersionComparisonSummary struct {
+ OperationTypes *[]OperationType `json:"operationTypes,omitempty"`
+ Refs *[]RefComparison `json:"refs,omitempty"`
+ Packages *map[string]PackageVersionRef `json:"packages,omitempty"`
+ NoContent bool `json:"noContent,omitempty"`
+}
+
+type RefComparison struct {
+ PackageRef string `json:"packageRef,omitempty"`
+ PreviousPackageRef string `json:"previousPackageRef,omitempty"`
+ OperationTypes []OperationType `json:"operationTypes"`
+ NoContent bool `json:"noContent,omitempty"`
+}
diff --git a/qubership-apihub-service/view/Content.go b/qubership-apihub-service/view/Content.go
new file mode 100644
index 0000000..6135cd8
--- /dev/null
+++ b/qubership-apihub-service/view/Content.go
@@ -0,0 +1,99 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/utils"
+)
+
+type Content struct {
+ FileId string `json:"fileId" validate:"required" msgpack:"fileId"`
+ Name string `json:"name" msgpack:"name"`
+ Type ShortcutType `json:"type" msgpack:"type"`
+ Path string `json:"path" msgpack:"path"`
+ Publish bool `json:"publish" msgpack:"publish"`
+ Status FileStatus `json:"status" msgpack:"status"`
+ LastStatus FileStatus `json:"lastStatus,omitempty" msgpack:"lastStatus,omitempty"`
+ MovedFrom string `json:"movedFrom,omitempty" msgpack:"movedFrom,omitempty"`
+ BlobId string `json:"blobId,omitempty" msgpack:"blobId,omitempty"`
+ ConflictedBlobId string `json:"conflictedBlobId,omitempty" msgpack:"conflictedBlobId,omitempty"`
+ ConflictedFileId string `json:"conflictedFileId,omitempty" msgpack:"conflictedFileId,omitempty"`
+ Labels []string `json:"labels,omitempty" msgpack:"labels,omitempty"`
+ Title string `json:"title,omitempty" msgpack:"title,omitempty"`
+ ChangeType ChangeType `json:"changeType,omitempty" msgpack:"changeType,omitempty"`
+ Included bool `json:"-"` //true if file was imported from git
+ FromFolder bool `json:"-"`
+ IsFolder bool `json:"-"`
+}
+
+type ContentGitConfigView struct {
+ FileId string `json:"fileId"` // git file path
+ Publish *bool `json:"publish,omitempty"` //pointer because absence of flag != false
+ Labels []string `json:"labels,omitempty"`
+}
+
+func TransformContentToGitView(content Content) ContentGitConfigView {
+ return ContentGitConfigView{
+ FileId: content.FileId,
+ Publish: &content.Publish,
+ Labels: content.Labels,
+ }
+}
+
+func TransformGitViewToContent(content ContentGitConfigView) Content {
+ publish := true
+ if content.Publish != nil {
+ publish = *content.Publish
+ }
+ labels := make([]string, 0)
+ if content.Labels != nil {
+ labels = content.Labels
+ }
+ fileId := utils.NormalizeFileId(content.FileId)
+ filePath, fileName := utils.SplitFileId(fileId)
+ return Content{
+ FileId: fileId,
+ Name: fileName,
+ Type: Unknown,
+ Path: filePath,
+ Publish: publish,
+ Status: StatusUnmodified,
+ Labels: labels,
+ }
+}
+
+type ContentAddResponse struct {
+ FileIds []string `json:"fileIds"`
+}
+
+func (c *Content) EqualsGitView(c2 *Content) bool {
+ return c.FileId == c2.FileId && c.Publish == c2.Publish && equalStringSets(c.Labels, c2.Labels)
+}
+
+func equalStringSets(first []string, second []string) bool {
+ if len(first) != len(second) {
+ return false
+ }
+ exists := make(map[string]bool)
+ for _, value := range first {
+ exists[value] = true
+ }
+ for _, value := range second {
+ if !exists[value] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/qubership-apihub-service/view/ContentChange.go b/qubership-apihub-service/view/ContentChange.go
new file mode 100644
index 0000000..b052902
--- /dev/null
+++ b/qubership-apihub-service/view/ContentChange.go
@@ -0,0 +1,22 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ContentChange struct {
+ Before *PublishedContentChange `json:"before"`
+ After *PublishedContentChange `json:"after"`
+ Action string `json:"action"`
+ Changes *ContentChanges `json:"changes,omitempty"`
+}
diff --git a/qubership-apihub-service/view/ContentChangeDetails.go b/qubership-apihub-service/view/ContentChangeDetails.go
new file mode 100644
index 0000000..758edb2
--- /dev/null
+++ b/qubership-apihub-service/view/ContentChangeDetails.go
@@ -0,0 +1,22 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ContentChanges struct {
+ Breaking *int `json:"breaking,omitempty"`
+ NonBreaking *int `json:"non-breaking,omitempty"`
+ Annotation *int `json:"annotation,omitempty"`
+ Unclassified *int `json:"unclassified,omitempty"`
+}
diff --git a/qubership-apihub-service/view/ContentData.go b/qubership-apihub-service/view/ContentData.go
new file mode 100644
index 0000000..971e916
--- /dev/null
+++ b/qubership-apihub-service/view/ContentData.go
@@ -0,0 +1,22 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ContentData struct {
+ FileId string
+ Data []byte
+ DataType string
+ BlobId string
+}
diff --git a/qubership-apihub-service/view/ContentMetaPatch.go b/qubership-apihub-service/view/ContentMetaPatch.go
new file mode 100644
index 0000000..a10ece5
--- /dev/null
+++ b/qubership-apihub-service/view/ContentMetaPatch.go
@@ -0,0 +1,20 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ContentMetaPatch struct {
+ Publish *bool `json:"publish,omitempty"`
+ Labels *[]string `json:"labels,omitempty"`
+}
diff --git a/qubership-apihub-service/view/DbCredentials.go b/qubership-apihub-service/view/DbCredentials.go
new file mode 100644
index 0000000..a192efb
--- /dev/null
+++ b/qubership-apihub-service/view/DbCredentials.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type DbCredentials struct {
+ Host string
+ Port int
+ Database string
+ Username string
+ Password string
+ SSLMode string
+}
diff --git a/qubership-apihub-service/view/Document.go b/qubership-apihub-service/view/Document.go
new file mode 100644
index 0000000..fc94036
--- /dev/null
+++ b/qubership-apihub-service/view/Document.go
@@ -0,0 +1,118 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "net/http"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/exception"
+)
+
+type DocumentTransformationReq struct {
+ PackageId string `json:"packageId" validate:"required"`
+ Version string `json:"version" validate:"required"`
+ ApiType string `json:"apiType" validate:"required"`
+ GroupName string `json:"groupName" validate:"required"`
+}
+
+type TransformedDocumentsFormat string
+
+const JsonDocumentFormat TransformedDocumentsFormat = "json"
+const YamlDocumentFormat TransformedDocumentsFormat = "yaml"
+const HtmlDocumentFormat TransformedDocumentsFormat = "html"
+
+func ValidTransformedDocumentsFormat_deprecated(format string) bool {
+ switch format {
+ case string(JsonDocumentFormat), string(HtmlDocumentFormat):
+ return true
+ }
+ return false
+}
+
+func ValidateTransformedDocumentsFormat(format string) error {
+ switch format {
+ case string(JsonDocumentFormat), string(HtmlDocumentFormat), string(YamlDocumentFormat):
+ return nil
+ }
+ return &exception.CustomError{
+ Status: http.StatusBadRequest,
+ Code: exception.InvalidParameterValue,
+ Message: exception.InvalidParameterValueMsg,
+ Params: map[string]interface{}{"param": "format", "value": format},
+ }
+}
+
+func ValidateFormatForBuildType(buildType string, format string) error {
+ err := ValidateGroupBuildType(buildType)
+ if err != nil {
+ return err
+ }
+ err = ValidateTransformedDocumentsFormat(format)
+ if err != nil {
+ return err
+ }
+ if buildType == MergedSpecificationType && format == string(HtmlDocumentFormat) {
+ return &exception.CustomError{
+ Status: http.StatusBadRequest,
+ Code: exception.FormatNotSupportedForBuildType,
+ Message: exception.FormatNotSupportedForBuildTypeMsg,
+ Params: map[string]interface{}{"format": format, "buildType": buildType},
+ }
+ }
+ return nil
+}
+
+type DocumentExtension string
+
+const JsonExtension DocumentExtension = "json"
+
+const (
+ JsonFormat string = "json"
+ YamlFormat string = "yaml"
+ MDFormat string = "md"
+ GraphQLFormat string = "graphql"
+ GQLFormat string = "gql"
+ ProtobufFormat string = "proto"
+ UnknownFormat string = "unknown"
+)
+
+func InvalidDocumentFormat(s string) bool {
+ switch s {
+ case JsonFormat, YamlFormat, MDFormat, GraphQLFormat, GQLFormat, ProtobufFormat, UnknownFormat:
+ return false
+ }
+ return true
+}
+
+const (
+ OpenAPI31Type string = "openapi-3-1"
+ OpenAPI30Type string = "openapi-3-0"
+ OpenAPI20Type string = "openapi-2-0"
+ Protobuf3Type string = "protobuf-3"
+ JsonSchemaType string = "json-schema"
+ MDType string = "markdown"
+ GraphQLSchemaType string = "graphql-schema"
+ GraphAPIType string = "graphapi"
+ IntrospectionType string = "introspection"
+ UnknownType string = "unknown"
+)
+
+func InvalidDocumentType(documentType string) bool {
+ switch documentType {
+ case OpenAPI31Type, OpenAPI30Type, OpenAPI20Type, Protobuf3Type, JsonSchemaType, MDType, GraphQLSchemaType, GraphAPIType, IntrospectionType, UnknownType:
+ return false
+ }
+ return true
+}
diff --git a/qubership-apihub-service/view/Excel.go b/qubership-apihub-service/view/Excel.go
new file mode 100644
index 0000000..3c3b542
--- /dev/null
+++ b/qubership-apihub-service/view/Excel.go
@@ -0,0 +1,40 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const SummarySheetName = "Summary"
+const RestAPISheetName = "REST API"
+const GraphQLSheetName = "GraphQL"
+const ProtobufSheetName = "Protobuf"
+const PackageIDColumnName = "Package ID"
+const PackageNameColumnName = "Package Name"
+const ServiceNameColumnName = "Service Name"
+const VersionColumnName = "Version"
+const PreviousVersionColumnName = "Previous Version"
+const APITypeColumnName = "API Type"
+const OperationTitleColumnName = "Operation Title"
+const OperationPathColumnName = "Operation Path"
+const OperationMethodColumnName = "Operation Method"
+const ChangeDescriptionColumnName = "Change Description"
+const ChangeSeverityColumnName = "Change Severity"
+const OperationTypeColumnName = "Operation Type"
+const TagColumnName = "Tag"
+const KindColumnName = "Kind"
+const DeprecatedColumnName = "Deprecated"
+const OperationActionColumnName = "Operation Action"
+const DeprecatedSinceColumnName = "Deprecated Since"
+const DeprecatedDescriptionColumnName = "Deprecated Description"
+const AdditionalInformationColumnName = "Additional Information"
+const APIKindColumnName = "API Kind"
diff --git a/qubership-apihub-service/view/Export.go b/qubership-apihub-service/view/Export.go
new file mode 100644
index 0000000..4fa6914
--- /dev/null
+++ b/qubership-apihub-service/view/Export.go
@@ -0,0 +1,52 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ExportApiChangesRequestView struct {
+ PreviousVersion string
+ PreviousVersionPackageId string
+ TextFilter string
+ Tags []string
+ ApiKind string
+ EmptyTag bool
+ RefPackageId string
+ Group string
+ EmptyGroup bool
+ ApiAudience string
+}
+
+type ExportOperationRequestView struct {
+ EmptyTag bool
+ Kind string
+ Tag string
+ TextFilter string
+ Tags []string
+ RefPackageId string
+ Group string
+ EmptyGroup bool
+ ApiAudience string
+}
+
+const ExportFormatXlsx = "xlsx"
+const ExportFormatJson = "json"
+
+func ValidateApiChangesExportFormat(format string) bool {
+ switch format {
+ case ExportFormatXlsx:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/qubership-apihub-service/view/ExternalMetadata.go b/qubership-apihub-service/view/ExternalMetadata.go
new file mode 100644
index 0000000..550edb8
--- /dev/null
+++ b/qubership-apihub-service/view/ExternalMetadata.go
@@ -0,0 +1,30 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type OperationExternalMetadataKey struct {
+ ApiType string `json:"apiType"`
+ Method string `json:"method"`
+ Path string `json:"path"`
+}
+
+type OperationExternalMetadata struct {
+ OperationExternalMetadataKey
+ ExternalMetadata map[string]interface{} `json:"externalMetadata"`
+}
+
+type ExternalMetadata struct {
+ Operations []OperationExternalMetadata `json:"operations"`
+}
diff --git a/qubership-apihub-service/view/FileChange.go b/qubership-apihub-service/view/FileChange.go
new file mode 100644
index 0000000..27692ef
--- /dev/null
+++ b/qubership-apihub-service/view/FileChange.go
@@ -0,0 +1,28 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type FileChange struct {
+ CommitId string `json:"commitId"`
+ ModifiedBy User `json:"modifiedBy"`
+ ModifiedAt time.Time `json:"modifiedAt"`
+ Comment string `json:"comment"`
+}
+
+type Changes struct {
+ Changes []FileChange `json:"changes"`
+}
diff --git a/qubership-apihub-service/view/FileStatus.go b/qubership-apihub-service/view/FileStatus.go
new file mode 100644
index 0000000..f49f3d4
--- /dev/null
+++ b/qubership-apihub-service/view/FileStatus.go
@@ -0,0 +1,70 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type FileStatus string
+
+const (
+ StatusAdded FileStatus = "added"
+ StatusIncluded FileStatus = "included"
+ StatusDeleted FileStatus = "deleted"
+ StatusExcluded FileStatus = "excluded"
+ StatusModified FileStatus = "modified"
+ StatusMoved FileStatus = "moved"
+ StatusUnmodified FileStatus = "unmodified"
+)
+
+func (f FileStatus) String() string {
+ switch f {
+ case StatusAdded:
+ return "added"
+ case StatusIncluded:
+ return "included"
+ case StatusDeleted:
+ return "deleted"
+ case StatusExcluded:
+ return "excluded"
+ case StatusModified:
+ return "modified"
+ case StatusMoved:
+ return "moved"
+ case StatusUnmodified:
+ return "unmodified"
+ default:
+ return ""
+ }
+
+}
+
+func ParseFileStatus(s string) FileStatus {
+ switch s {
+ case "added":
+ return StatusAdded
+ case "included":
+ return StatusIncluded
+ case "deleted":
+ return StatusDeleted
+ case "excluded":
+ return StatusExcluded
+ case "modified":
+ return StatusModified
+ case "moved":
+ return StatusMoved
+ case "unmodified":
+ return StatusUnmodified
+ default:
+ return ""
+ }
+}
diff --git a/qubership-apihub-service/view/GitBranch.go b/qubership-apihub-service/view/GitBranch.go
new file mode 100644
index 0000000..7c4a245
--- /dev/null
+++ b/qubership-apihub-service/view/GitBranch.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type GitBranch struct {
+ Name string `json:"name"`
+}
+
+type GitBranches struct {
+ Branches []GitBranch `json:"branches"`
+}
diff --git a/qubership-apihub-service/view/GitCommit.go b/qubership-apihub-service/view/GitCommit.go
new file mode 100644
index 0000000..493d939
--- /dev/null
+++ b/qubership-apihub-service/view/GitCommit.go
@@ -0,0 +1,25 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type GitCommit struct {
+ Id string
+ CommitterName string
+ CommitterEmail string
+ CommittedDate time.Time
+ Message string
+}
diff --git a/qubership-apihub-service/view/GitFiles.go b/qubership-apihub-service/view/GitFiles.go
new file mode 100644
index 0000000..5bb1ef1
--- /dev/null
+++ b/qubership-apihub-service/view/GitFiles.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type FileNode struct {
+ Name string `json:"name"`
+ IsFolder bool `json:"isFolder"`
+}
+
+type ListFilesView struct {
+ Files []FileNode `json:"files"`
+}
diff --git a/qubership-apihub-service/view/GitIntegration.go b/qubership-apihub-service/view/GitIntegration.go
new file mode 100644
index 0000000..ac4e558
--- /dev/null
+++ b/qubership-apihub-service/view/GitIntegration.go
@@ -0,0 +1,38 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "net/http"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/exception"
+)
+
+type GitIntegrationType string
+
+const GitlabIntegration GitIntegrationType = "gitlab"
+const UnknownIntegration GitIntegrationType = "unknown"
+
+func GitIntegrationTypeFromStr(str string) (GitIntegrationType, error) {
+ if str == "gitlab" {
+ return GitlabIntegration, nil
+ }
+ return UnknownIntegration, &exception.CustomError{
+ Status: http.StatusBadRequest,
+ Code: exception.UnknownIntegrationType,
+ Message: exception.UnknownIntegrationTypeMsg,
+ Params: map[string]interface{}{"type": str},
+ }
+}
diff --git a/qubership-apihub-service/view/GitRepository.go b/qubership-apihub-service/view/GitRepository.go
new file mode 100644
index 0000000..6991789
--- /dev/null
+++ b/qubership-apihub-service/view/GitRepository.go
@@ -0,0 +1,30 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type GitRepository struct {
+ RepositoryId string `json:"repositoryId"`
+ Name string `json:"name"`
+ DefaultBranch string `json:"defaultBranch"`
+}
+
+type GitGroup struct {
+ Name string `json:"name"`
+}
+
+type RepositoriesList struct {
+ Repositories []GitRepository `json:"repositories"`
+ Groups []GitGroup `json:"groups"`
+}
diff --git a/qubership-apihub-service/view/GitVersionPublish.go b/qubership-apihub-service/view/GitVersionPublish.go
new file mode 100644
index 0000000..8fddd95
--- /dev/null
+++ b/qubership-apihub-service/view/GitVersionPublish.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type GitVersionPublish struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Status string `json:"status"`
+}
diff --git a/qubership-apihub-service/view/GraphQLOperation.go b/qubership-apihub-service/view/GraphQLOperation.go
new file mode 100644
index 0000000..dc5f244
--- /dev/null
+++ b/qubership-apihub-service/view/GraphQLOperation.go
@@ -0,0 +1,63 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const (
+ QueryType string = "query"
+ MutationType string = "mutation"
+ SubscriptionType string = "subscription"
+)
+
+func ValidGraphQLOperationType(typeValue string) bool {
+ switch typeValue {
+ case QueryType, MutationType, SubscriptionType:
+ return true
+ }
+ return false
+}
+
+type GraphQLOperationMetadata struct {
+ Type string `json:"type"`
+ Method string `json:"method"`
+ Tags []string `json:"tags"`
+}
+
+type GraphQLOperationSingleView struct {
+ SingleOperationView
+ GraphQLOperationMetadata
+}
+
+type GraphQLOperationView struct {
+ OperationListView
+ GraphQLOperationMetadata
+}
+type DeprecateGraphQLOperationView struct {
+ DeprecatedOperationView
+ GraphQLOperationMetadata
+}
+
+type GraphQLOperationComparisonChangelogView_deprecated struct {
+ OperationComparisonChangelogView_deprecated
+ GraphQLOperationMetadata
+}
+
+type GraphQLOperationComparisonChangelogView struct {
+ OperationComparisonChangelogView
+ GraphQLOperationMetadata
+}
+type GraphQLOperationComparisonChangesView struct {
+ OperationComparisonChangesView
+ GraphQLOperationMetadata
+}
diff --git a/qubership-apihub-service/view/Group.go b/qubership-apihub-service/view/Group.go
new file mode 100644
index 0000000..1ab7e4d
--- /dev/null
+++ b/qubership-apihub-service/view/Group.go
@@ -0,0 +1,54 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type Group struct {
+ Id string `json:"groupId"`
+ Name string `json:"name" validate:"required"`
+ Alias string `json:"alias" validate:"required"` // short alias
+ ParentId string `json:"parentId"`
+ ImageUrl string `json:"imageUrl"`
+ Description string `json:"description"`
+ CreatedBy string `json:"-"`
+ CreatedAt time.Time `json:"-"`
+ DeletedAt *time.Time `json:"-"`
+ DeletedBy string `json:"-"`
+ IsFavorite bool `json:"isFavorite"`
+ LastVersion string `json:"lastVersion,omitempty"` // Required only for group list
+}
+
+type GroupInfo struct {
+ GroupId string `json:"groupId"`
+ ParentId string `json:"parentId"`
+ Name string `json:"name"`
+ Alias string `json:"alias"` // short alias
+ ImageUrl string `json:"imageUrl"`
+ Parents []Group `json:"parents"`
+ IsFavorite bool `json:"isFavorite"`
+ LastVersion string `json:"lastVersion,omitempty"`
+}
+
+type Groups struct {
+ Groups []Group `json:"groups"`
+}
+
+type PublishGroupRequest struct {
+ Version string `json:"version"`
+ PreviousVersion string `json:"previousVersion"`
+ Status string `json:"status"`
+ Refs []Ref `json:"refs"`
+}
diff --git a/qubership-apihub-service/view/Integration.go b/qubership-apihub-service/view/Integration.go
new file mode 100644
index 0000000..a5d9123
--- /dev/null
+++ b/qubership-apihub-service/view/Integration.go
@@ -0,0 +1,32 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ExternalIntegration string
+
+const ExternalSamlIntegration ExternalIntegration = "saml"
+const ExternalGitlabIntegration ExternalIntegration = "gitlab"
+const ExternalLdapIntegration ExternalIntegration = "ldap"
+
+func GetIntegrationExternalId(user User, integration ExternalIntegration) string {
+ switch integration {
+ case ExternalSamlIntegration,
+ ExternalGitlabIntegration,
+ ExternalLdapIntegration:
+ return user.Id
+ default:
+ return ""
+ }
+}
diff --git a/qubership-apihub-service/view/LdapAttributes.go b/qubership-apihub-service/view/LdapAttributes.go
new file mode 100644
index 0000000..b171575
--- /dev/null
+++ b/qubership-apihub-service/view/LdapAttributes.go
@@ -0,0 +1,21 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const Mail string = "mail"
+const DisplayName string = "displayName"
+const Surname string = "sn"
+const SAMAccountName string = "sAMAccountName"
+const ThumbnailPhoto string = "thumbnailPhoto"
diff --git a/qubership-apihub-service/view/Minio.go b/qubership-apihub-service/view/Minio.go
new file mode 100644
index 0000000..ecc5834
--- /dev/null
+++ b/qubership-apihub-service/view/Minio.go
@@ -0,0 +1,28 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type MinioStorageCreds struct {
+ BucketName string
+ IsActive bool
+ Endpoint string
+ Crt string
+ AccessKeyId string
+ SecretAccessKey string
+ IsOnlyForBuildResult bool
+}
+
+const PUBLISHED_SOURCES_ARCHIVES_TABLE = "published_sources_archives"
+const BUILD_RESULT_TABLE = "build_result"
diff --git a/qubership-apihub-service/view/Monitoring.go b/qubership-apihub-service/view/Monitoring.go
new file mode 100644
index 0000000..7fa637e
--- /dev/null
+++ b/qubership-apihub-service/view/Monitoring.go
@@ -0,0 +1,38 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type SearchEndpointOpts struct {
+ SearchLevel string `json:"searchLevel,omitempty"`
+ ApiType string `json:"apiType,omitempty"`
+ Scopes []string `json:"scope,omitempty"`
+ DetailedScopes []string `json:"detailedScope,omitempty"`
+ Methods []string `json:"methods,omitempty"`
+ OperationTypes []string `json:"operationTypes,omitempty"`
+}
+
+func MakeSearchEndpointOptions(searchLevel string, operationSearchParams *OperationSearchParams) SearchEndpointOpts {
+ searchOpts := SearchEndpointOpts{
+ SearchLevel: searchLevel,
+ }
+ if operationSearchParams != nil {
+ searchOpts.ApiType = operationSearchParams.ApiType
+ searchOpts.Scopes = operationSearchParams.Scopes
+ searchOpts.DetailedScopes = operationSearchParams.DetailedScopes
+ searchOpts.Methods = operationSearchParams.Methods
+ searchOpts.OperationTypes = operationSearchParams.OperationTypes
+ }
+ return searchOpts
+}
diff --git a/qubership-apihub-service/view/Oauth2.go b/qubership-apihub-service/view/Oauth2.go
new file mode 100644
index 0000000..154749e
--- /dev/null
+++ b/qubership-apihub-service/view/Oauth2.go
@@ -0,0 +1,36 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type OAuthAccessResponse struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ ExpiresIn int `json:"expires_in"`
+ RefreshToken string `json:"refresh_token"`
+ CreatedAt int `json:"created_at"`
+ Error string `json:"error"`
+}
+
+func GetTokenExpirationDate(expiresIn int) time.Time {
+ var duration time.Duration
+ if expiresIn == 0 {
+ duration = time.Hour * 2 //default gitlab token expiration time
+ } else {
+ duration = time.Duration(expiresIn) * time.Second
+ }
+ return time.Now().Add(duration)
+}
diff --git a/qubership-apihub-service/view/OperationGroup.go b/qubership-apihub-service/view/OperationGroup.go
new file mode 100644
index 0000000..b3c16c5
--- /dev/null
+++ b/qubership-apihub-service/view/OperationGroup.go
@@ -0,0 +1,109 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "fmt"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/utils"
+)
+
+const OperationGroupOperationsLimit = 5000
+const OperationGroupActionCreate = "create"
+const OperationGroupActionUpdate = "update"
+const OperationGroupActionDelete = "delete"
+
+type CreateOperationGroupReq_deprecated struct {
+ GroupName string `json:"groupName" validate:"required"`
+ Description string `json:"description"`
+}
+
+type ReplaceOperationGroupReq_deprecated struct {
+ CreateOperationGroupReq_deprecated
+ Operations []GroupOperations `json:"operations" validate:"dive,required"`
+}
+
+type CreateOperationGroupReq struct {
+ GroupName string `json:"groupName" validate:"required"`
+ Description string `json:"description"`
+ Template []byte `json:"template"`
+ TemplateFilename string `json:"templateFilename"`
+}
+
+type ReplaceOperationGroupReq struct {
+ CreateOperationGroupReq
+ Operations []GroupOperations `json:"operations" validate:"dive,required"`
+}
+
+type UpdateOperationGroupReq_deprecated struct {
+ GroupName *string `json:"groupName"`
+ Description *string `json:"description"`
+}
+
+type UpdateOperationGroupReq struct {
+ GroupName *string
+ Description *string
+ Template *OperationGroupTemplate
+ Operations *[]GroupOperations `json:"operations" validate:"dive,required"`
+}
+
+type OperationGroupTemplate struct {
+ TemplateData []byte
+ TemplateFilename string
+}
+
+type GroupOperations struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ OperationId string `json:"operationId" validate:"required"`
+}
+
+type OperationGroups struct {
+ OperationGroups []OperationGroup `json:"operationGroups"`
+}
+
+type OperationGroup struct {
+ GroupName string `json:"groupName"`
+ Description string `json:"description,omitempty"`
+ IsPrefixGroup bool `json:"isPrefixGroup"`
+ OperationsCount int `json:"operationsCount"`
+}
+
+type CalculatedOperationGroups struct {
+ Groups []string `json:"groups"`
+}
+
+func MakeOperationGroupId(packageId string, version string, revision int, apiType string, groupName string) string {
+ uniqueString := fmt.Sprintf("%v@%v@%v@%v@%v", packageId, version, revision, apiType, groupName)
+ return utils.GetEncodedChecksum([]byte(uniqueString))
+}
+
+type OperationGroupPublishReq struct {
+ PackageId string `json:"packageId" validate:"required"`
+ Version string `json:"version" validate:"required"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Status string `json:"status" validate:"required"`
+ VersionLabels []string `json:"versionLabels"`
+}
+
+type OperationGroupPublishResp struct {
+ PublishId string `json:"publishId"`
+}
+
+type OperationGroupPublishStatusResponse struct {
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
diff --git a/qubership-apihub-service/view/Package.go b/qubership-apihub-service/view/Package.go
new file mode 100644
index 0000000..6c661d1
--- /dev/null
+++ b/qubership-apihub-service/view/Package.go
@@ -0,0 +1,341 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/utils"
+)
+
+type Package struct {
+ Id string `json:"projectId"` //todo replace with packageId
+ GroupId string `json:"groupId"` //todo replace with parentId
+ Name string `json:"name"`
+ Alias string `json:"alias"`
+ Description string `json:"description"`
+ IsFavorite bool `json:"isFavorite"`
+ Groups []Group `json:"groups"`
+ DeletionDate *time.Time `json:"-"`
+ DeletedBy string `json:"-"`
+ CreatedBy string `json:"-"`
+ CreatedAt time.Time `json:"-"`
+ ServiceName string `json:"serviceName,omitempty"`
+ LastVersion string `json:"lastVersion,omitempty"`
+}
+
+type Packages_deprecated struct {
+ Packages []Package `json:"projects"` //todo replace with packages
+}
+
+type PackageInfo struct {
+ PackageId string `json:"packageId" validate:"required"`
+ ParentId string `json:"-"`
+ Alias string `json:"-"`
+ Version string `json:"version" validate:"required"`
+ ServiceName string `json:"serviceName"`
+ Folder string `json:"folder"`
+ PackageName string `json:"packageName"` // TODO: not used?
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Status string `json:"status" validate:"required"`
+ Refs []PackageInfoRef `json:"refs" validate:"dive,required"`
+ Files []PackageInfoFile `json:"files" validate:"dive,required"`
+ VersionLabels []string `json:"versionLabels"`
+ BranchName string `json:"branchName,omitempty"`
+ RepositoryUrl string `json:"repositoryUrl,omitempty"`
+}
+
+type PackageInfoRef struct {
+ RefPackageId string `json:"refId" validate:"required"`
+ RefVersion string `json:"version" validate:"required"`
+}
+
+type SimplePackage struct {
+ Id string `json:"packageId"`
+ Alias string `json:"alias" validate:"required"`
+ ParentId string `json:"parentId"`
+ Kind string `json:"kind" validate:"required"`
+ Name string `json:"name" validate:"required"`
+ Description string `json:"description"`
+ IsFavorite bool `json:"isFavorite"`
+ ServiceName string `json:"serviceName,omitempty"`
+ ImageUrl string `json:"imageUrl"`
+ Parents []ParentPackageInfo `json:"parents"`
+ DefaultRole string `json:"defaultRole"`
+ UserPermissions []string `json:"permissions"`
+ DeletionDate *time.Time `json:"-"`
+ DeletedBy string `json:"-"`
+ CreatedBy string `json:"-"`
+ CreatedAt time.Time `json:"-"`
+ DefaultReleaseVersion string `json:"defaultReleaseVersion"`
+ DefaultVersion string `json:"defaultVersion"`
+ ReleaseVersionPattern string `json:"releaseVersionPattern"`
+ ExcludeFromSearch *bool `json:"excludeFromSearch,omitempty"`
+ RestGroupingPrefix string `json:"restGroupingPrefix,omitempty"`
+}
+
+type GlobalPackage struct {
+ PackageId string `json:"packageId"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ ParentPackages []SimplePackage `json:"parentPackages"`
+}
+
+type Packages struct {
+ Packages []PackagesInfo `json:"packages"`
+}
+
+type PackagesInfo struct {
+ Id string `json:"packageId"`
+ Alias string `json:"alias"`
+ ParentId string `json:"parentId"`
+ Kind string `json:"kind"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ IsFavorite bool `json:"isFavorite"`
+ ServiceName string `json:"serviceName,omitempty"`
+ ImageUrl string `json:"imageUrl"`
+ Parents []ParentPackageInfo `json:"parents"`
+ DefaultRole string `json:"defaultRole"`
+ UserPermissions []string `json:"permissions"`
+ LastReleaseVersionDetails *VersionDetails `json:"lastReleaseVersionDetails,omitempty"`
+ RestGroupingPrefix string `json:"restGroupingPrefix,omitempty"`
+ ReleaseVersionPattern string `json:"releaseVersionPattern,omitempty"`
+}
+
+type ParentPackageInfo struct {
+ Id string `json:"packageId"`
+ Alias string `json:"alias"`
+ ParentId string `json:"parentId"`
+ Kind string `json:"kind"`
+ Name string `json:"name"`
+ ImageUrl string `json:"imageUrl"`
+ HasReadPermission *bool `json:"hasReadPermission,omitempty"`
+}
+
+type VersionDetails struct {
+ Version string `json:"version"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+ Summary *ChangeSummary `json:"summary,omitempty"`
+}
+type PackageListReq struct {
+ Kind []string
+ Limit int
+ OnlyFavorite bool
+ OnlyShared bool
+ Offset int
+ ParentId string
+ ShowParents bool
+ TextFilter string
+ LastReleaseVersionDetails bool
+ ServiceName string
+ ShowAllDescendants bool
+ Ids []string
+}
+
+type PatchPackageReq struct {
+ Name *string `json:"name"`
+ Description *string `json:"description"`
+ ServiceName *string `json:"serviceName"`
+ ImageUrl *string `json:"imageUrl"`
+ DefaultRole *string `json:"defaultRole"`
+ DefaultReleaseVersion *string `json:"defaultReleaseVersion"`
+ ReleaseVersionPattern *string `json:"releaseVersionPattern"`
+ ExcludeFromSearch *bool `json:"excludeFromSearch"`
+ RestGroupingPrefix *string `json:"restGroupingPrefix"`
+}
+
+// build result
+type PackageInfoFile struct {
+ PackageId string `json:"packageId" validate:"required"`
+ Kind string `json:"-"`
+ BuildType string `json:"buildType"`
+ Version string `json:"version" validate:"required"`
+ Status string `json:"status" validate:"required"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Metadata map[string]interface{} `json:"metadata"`
+ Refs []BCRef `json:"refs"`
+ Revision int `json:"-"`
+ PreviousVersionRevision int `json:"-"`
+ CreatedBy string `json:"createdBy"`
+ BuilderVersion string `json:"builderVersion"`
+ PublishedAt *time.Time `json:"publishedAt"` //for migration
+ MigrationBuild bool `json:"migrationBuild"` //for migration
+ MigrationId string `json:"migrationId"` //for migration
+ NoChangelog bool `json:"noChangeLog,omitempty"` //for migration
+ ApiType string `json:"apiType"`
+ GroupName string `json:"groupName"`
+ Format string `json:"format"`
+ ExternalMetadata *ExternalMetadata `json:"externalMetadata,omitempty"`
+}
+
+type ChangelogInfoFile struct {
+ BuildType string `json:"buildType"`
+ PackageId string `json:"packageId" validate:"required"`
+ Version string `json:"version" validate:"required"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId" validate:"required"`
+ PreviousVersion string `json:"previousVersion" validate:"required"`
+ Metadata map[string]interface{} `json:"metadata"`
+ Revision int `json:"revision"`
+ PreviousVersionRevision int `json:"previousVersionRevision"`
+ CreatedBy string `json:"createdBy"`
+ BuilderVersion string `json:"builderVersion"`
+ PublishedAt *time.Time `json:"publishedAt"` //for migration
+}
+
+func MakeChangelogInfoFileView(packageInfo PackageInfoFile) ChangelogInfoFile {
+ return ChangelogInfoFile{
+ BuildType: packageInfo.BuildType,
+ PackageId: packageInfo.PackageId,
+ Version: packageInfo.Version,
+ PreviousVersionPackageId: packageInfo.PreviousVersionPackageId,
+ PreviousVersion: packageInfo.PreviousVersion,
+ Metadata: packageInfo.Metadata,
+ Revision: packageInfo.Revision,
+ PreviousVersionRevision: packageInfo.PreviousVersionRevision,
+ CreatedBy: packageInfo.CreatedBy,
+ BuilderVersion: packageInfo.BuilderVersion,
+ PublishedAt: packageInfo.PublishedAt,
+ }
+}
+
+type PackageOperationsFile struct {
+ Operations []Operation `json:"operations" validate:"dive,required"`
+}
+
+type PackageDocumentsFile struct {
+ Documents []PackageDocument `json:"documents" validate:"dive,required"`
+}
+
+type PackageOperationChanges struct {
+ OperationComparisons []OperationComparison `json:"operations" validate:"dive,required"`
+}
+
+type PackageComparisonsFile struct {
+ Comparisons []VersionComparison `json:"comparisons" validate:"dive,required"`
+}
+
+type VersionComparison struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionRevision int `json:"previousVersionRevision"`
+ OperationTypes []OperationType `json:"operationTypes" validate:"required,dive,required"`
+ FromCache bool `json:"fromCache"`
+ ComparisonFileId string `json:"comparisonFileId"`
+}
+
+func MakeVersionComparisonId(packageId string, version string, revision int, previousVersionPackageId string, previousVersion string, previousVersionRevision int) string {
+ uniqueString := fmt.Sprintf("%v@%v@%v@%v@%v@%v", packageId, version, revision, previousVersionPackageId, previousVersion, previousVersionRevision)
+ return utils.GetEncodedChecksum([]byte(uniqueString))
+}
+
+type OperationType struct {
+ ApiType string `json:"apiType" validate:"required"`
+ ChangesSummary ChangeSummary `json:"changesSummary" validate:"required"`
+ NumberOfImpactedOperations ChangeSummary `json:"numberOfImpactedOperations"`
+ ApiAudienceTransitions []ApiAudienceTransition `json:"apiAudienceTransitions,omitempty"`
+ Tags []string `json:"tags"`
+}
+
+type ApiAudienceTransition struct {
+ CurrentAudience string `json:"currentAudience"`
+ PreviousAudience string `json:"previousAudience"`
+ OperationsCount int `json:"operationsCount"`
+}
+
+type BuilderNotificationsFile struct {
+ Notifications []BuilderNotification `json:"notifications" validate:"dive,required"`
+}
+
+type PackageRef struct {
+ RefId string `json:"refId"`
+ Version string `json:"version"`
+}
+
+type PackageDocument struct {
+ FileId string `json:"fileId" validate:"required"`
+ Type string `json:"type" validate:"required"`
+ Slug string `json:"slug" validate:"required"`
+ Title string `json:"title" validate:"required"`
+ Description string `json:"description"`
+ Version string `json:"version"`
+ OperationIds []string `json:"operationIds" validate:"required"`
+ Metadata map[string]interface{} `json:"metadata"`
+ Filename string `json:"filename" validate:"required"`
+ Format string `json:"format"`
+}
+
+type BuilderNotification struct {
+ Severity int `json:"severity"`
+ Message string `json:"message"`
+ FileId string `json:"fileId"`
+}
+
+const PackageGroupingPrefixWildcard = "{group}"
+
+func regexpEscaped(s string) string {
+ reservedChars := `\!$()*+.:<=>?[]^{|}-`
+ escapeChar := `\`
+ for _, c := range reservedChars {
+ s = strings.ReplaceAll(s, string(c), escapeChar+string(c))
+ }
+ return s
+}
+
+func MakePackageGroupingPrefixRegex(groupingPrefix string) string {
+ groupingPrefix = regexpEscaped(groupingPrefix)
+ groupingPrefix = strings.Replace(groupingPrefix, regexpEscaped(PackageGroupingPrefixWildcard), `(.*?)`, 1)
+ groupingPrefix = "^" + groupingPrefix
+ return groupingPrefix
+}
+
+func MakePackageRefKey(packageId string, version string, revision int) string {
+ if packageId == "" || version == "" || revision == 0 {
+ return ""
+ }
+ return fmt.Sprintf("%v@%v@%v", packageId, version, revision)
+}
+
+func MakeVersionRefKey(version string, revision int) string {
+ if version == "" || revision == 0 {
+ return ""
+ }
+ return fmt.Sprintf("%v@%v", version, revision)
+}
+
+func MakePackageVersionRefKey(packageId string, version string) string {
+ if packageId == "" || version == "" {
+ return ""
+ }
+ return fmt.Sprintf("%v@%v", packageId, version)
+}
+
+type PackageV2 struct {
+ Id string `json:"id"`
+ Alias string `json:"alias" validate:"required"`
+ Name string `json:"name" validate:"required"`
+ Kind string `json:"kind" validate:"required"`
+ ParentId string `json:"parentId" validate:"required"`
+ Description string `json:"description"`
+ ServiceName string `json:"serviceName"`
+ ImageUrl string `json:"imageUrl"`
+}
diff --git a/qubership-apihub-service/view/PackageMemberRole.go b/qubership-apihub-service/view/PackageMemberRole.go
new file mode 100644
index 0000000..efa090e
--- /dev/null
+++ b/qubership-apihub-service/view/PackageMemberRole.go
@@ -0,0 +1,55 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const ActionAddRole = "add"
+const ActionRemoveRole = "remove"
+
+type PackageMemberRoleView struct {
+ RoleId string `json:"roleId"`
+ RoleName string `json:"role"`
+ Inheritance *ShortPackage `json:"inheritance,omitempty"`
+}
+
+type PackageMember struct {
+ User User `json:"user"`
+ Roles []PackageMemberRoleView `json:"roles"`
+}
+
+type PackageMembers struct {
+ Members []PackageMember `json:"members"`
+}
+
+type ShortPackage struct {
+ PackageId string `json:"packageId"`
+ Kind string `json:"kind"`
+ Name string `json:"name"`
+}
+
+type AvailablePackagePromoteStatuses map[string][]string // map[packageId][]version status
+
+type PackageMembersAddReq struct {
+ Emails []string `json:"emails" validate:"required"`
+ RoleIds []string `json:"roleIds" validate:"required"`
+}
+
+type PackageMemberUpdatePatch struct {
+ RoleId string `json:"roleId" validate:"required"`
+ Action string `json:"action" validate:"required"`
+}
+
+type PackagesReq struct {
+ Packages []string `json:"packages"`
+}
diff --git a/qubership-apihub-service/view/Paging.go b/qubership-apihub-service/view/Paging.go
new file mode 100644
index 0000000..2f1e15a
--- /dev/null
+++ b/qubership-apihub-service/view/Paging.go
@@ -0,0 +1,28 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Pageable struct {
+ Content interface{} `json:"content"`
+ TotalItems int `json:"totalItems"`
+ TotalPages int `json:"totalPages"`
+ ItemsPerPage int `json:"itemsPerPage"`
+ CurrentPage int `json:"currentPage"`
+}
+
+type PagingParams struct {
+ Page int `json:"page"`
+ ItemsPerPage int `json:"itemsPerPage"`
+}
diff --git a/qubership-apihub-service/view/Portal.go b/qubership-apihub-service/view/Portal.go
new file mode 100644
index 0000000..4e7f189
--- /dev/null
+++ b/qubership-apihub-service/view/Portal.go
@@ -0,0 +1,60 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type DocumentationType string
+
+const DTInteractive DocumentationType = "INTERACTIVE"
+const DTStatic DocumentationType = "STATIC"
+const DTPdf DocumentationType = "PDF"
+const DTRaw DocumentationType = "RAW"
+
+func GetDtFromStr(str string) DocumentationType {
+ switch str {
+ case "INTERACTIVE":
+ return DTInteractive
+ case "STATIC":
+ return DTStatic
+ case "PDF":
+ return DTPdf
+ case "RAW":
+ return DTRaw
+ case "":
+ return DTInteractive
+ }
+ return DocumentationType(str)
+}
+
+type VersionDocMetadata struct {
+ GitLink string `json:"gitLink"`
+ Branch string `json:"branch"`
+ DateOfPublication string `json:"dateOfPublication"`
+ CommitId string `json:"commitId"`
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ User string `json:"user"`
+ Labels []string `json:"labels"`
+ Files []FileMetadata `json:"files"`
+}
+
+type FileMetadata struct {
+ Type string `json:"type"`
+ Name string `json:"name"` // title
+ Format string `json:"format"`
+ Slug string `json:"slug"`
+ Labels []string `json:"labels,omitempty"`
+ Openapi *Openapi `json:"openapi,omitempty"`
+ Asyncapi *Asyncapi `json:"asyncapi,omitempty"`
+}
diff --git a/qubership-apihub-service/view/Principal.go b/qubership-apihub-service/view/Principal.go
new file mode 100644
index 0000000..972f811
--- /dev/null
+++ b/qubership-apihub-service/view/Principal.go
@@ -0,0 +1,28 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PrincipalUserView struct {
+ PrincipalType PrincipalType `json:"type"`
+ User
+}
+type PrincipalApiKeyView struct {
+ PrincipalType PrincipalType `json:"type"`
+ ApiKey
+}
+type PrincipalType string
+
+const PTUser PrincipalType = "user"
+const PTApiKey PrincipalType = "apiKey"
diff --git a/qubership-apihub-service/view/Project.go b/qubership-apihub-service/view/Project.go
new file mode 100644
index 0000000..f2bc16e
--- /dev/null
+++ b/qubership-apihub-service/view/Project.go
@@ -0,0 +1,50 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type Project struct {
+ Id string `json:"projectId"`
+ GroupId string `json:"groupId" validate:"required"`
+ Name string `json:"name" validate:"required"`
+ Alias string `json:"alias" validate:"required"` // short alias
+ Description string `json:"description"`
+ IsFavorite bool `json:"isFavorite"`
+ Integration IntegrationView `json:"integration"`
+ Groups []Group `json:"groups"`
+ DeletionDate *time.Time `json:"-"`
+ DeletedBy string `json:"-"`
+ LastVersion string `json:"lastVersion,omitempty"`
+
+ PackageId string `json:"packageId"`
+}
+
+type IntegrationView struct {
+ Type GitIntegrationType `json:"type" validate:"required"`
+ RepositoryId string `json:"repositoryId" validate:"required"`
+ RepositoryName string `json:"repositoryName"`
+ RepositoryUrl string `json:"repositoryUrl"`
+ DefaultBranch string `json:"defaultBranch" validate:"required"`
+ DefaultFolder string `json:"defaultFolder" validate:"required"`
+}
+
+type Projects struct {
+ Projects []Project `json:"projects"`
+}
+
+type GitLabWebhookIntegration struct {
+ SecretToken string `json:"secretToken" validate:"required"`
+}
diff --git a/qubership-apihub-service/view/ProtobufOperation.go b/qubership-apihub-service/view/ProtobufOperation.go
new file mode 100644
index 0000000..06de9d1
--- /dev/null
+++ b/qubership-apihub-service/view/ProtobufOperation.go
@@ -0,0 +1,58 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const (
+ UnaryType string = "unary"
+ ServerStreamingType string = "serverStreaming"
+ ClientStreamingType string = "clientStreaming"
+ BidirectionalStreamingType string = "bidirectionalStreaming"
+)
+
+func ValidProtobufOperationType(typeValue string) bool {
+ switch typeValue {
+ case UnaryType, ServerStreamingType, ClientStreamingType, BidirectionalStreamingType:
+ return true
+ }
+ return false
+}
+
+type ProtobufOperationMetadata struct {
+ Type string `json:"type"`
+ Method string `json:"method"`
+}
+
+type ProtobufOperationSingleView struct {
+ SingleOperationView
+ ProtobufOperationMetadata
+}
+
+type ProtobufOperationView struct {
+ OperationListView
+ ProtobufOperationMetadata
+}
+type DeprecateProtobufOperationView struct {
+ DeprecatedOperationView
+ ProtobufOperationMetadata
+}
+
+type ProtobufOperationComparisonChangelogView struct {
+ OperationComparisonChangelogView
+ ProtobufOperationMetadata
+}
+type ProtobufOperationComparisonChangesView struct {
+ OperationComparisonChangesView
+ ProtobufOperationMetadata
+}
diff --git a/qubership-apihub-service/view/PublicKey.go b/qubership-apihub-service/view/PublicKey.go
new file mode 100644
index 0000000..709cc89
--- /dev/null
+++ b/qubership-apihub-service/view/PublicKey.go
@@ -0,0 +1,19 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PublicKey struct {
+ Value []byte
+}
diff --git a/qubership-apihub-service/view/PublishV2.go b/qubership-apihub-service/view/PublishV2.go
new file mode 100644
index 0000000..afc224a
--- /dev/null
+++ b/qubership-apihub-service/view/PublishV2.go
@@ -0,0 +1,20 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PublishV2Response struct {
+ PublishId string `json:"publishId"`
+ Config *BuildConfig `json:"config,omitempty"`
+}
diff --git a/qubership-apihub-service/view/PublishedBranchView.go b/qubership-apihub-service/view/PublishedBranchView.go
new file mode 100644
index 0000000..f7052bb
--- /dev/null
+++ b/qubership-apihub-service/view/PublishedBranchView.go
@@ -0,0 +1,29 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type BranchItemView struct {
+ Name string `json:"name"`
+ Version string `json:"version,omitempty"`
+ Status VersionStatus `json:"status,omitempty"`
+ PublishedAt *time.Time `json:"publishedAt,omitempty"`
+ Permissions []string `json:"permissions"`
+}
+
+type BranchListView struct {
+ Branches []BranchItemView `json:"branches"`
+}
diff --git a/qubership-apihub-service/view/PublishedContent.go b/qubership-apihub-service/view/PublishedContent.go
new file mode 100644
index 0000000..c898243
--- /dev/null
+++ b/qubership-apihub-service/view/PublishedContent.go
@@ -0,0 +1,132 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PublishedContent struct {
+ ContentId string `json:"fileId"`
+ Type ShortcutType `json:"type"`
+ Format string `json:"format"`
+ Path string `json:"-"`
+ Name string `json:"-"`
+ Index int `json:"-"`
+ Slug string `json:"slug"`
+ Labels []string `json:"labels,omitempty"`
+ Title string `json:"title,omitempty"`
+ Version string `json:"version,omitempty"`
+ ReferenceId string `json:"refId,omitempty"`
+ Openapi *Openapi `json:"openapi,omitempty"`
+ Asyncapi *Asyncapi `json:"asyncapi,omitempty"`
+}
+
+type PublishedContentInfo struct {
+ FileId string
+ Checksum string
+}
+
+type SharedUrlResult_deprecated struct {
+ SharedId string `json:"sharedId"`
+}
+
+// deprecated
+type PublishedDocument_deprecated struct {
+ FieldId string `json:"fileId"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Format string `json:"format"`
+ Title string `json:"title,omitempty"`
+ Labels []string `json:"labels,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Info interface{} `json:"info,omitempty"`
+ ExternalDocs interface{} `json:"externalDocs,omitempty"`
+ Operations []DocumentsOperation_deprecated `json:"operations,omitempty"`
+ Filename string `json:"filename"`
+ Tags []interface{} `json:"tags"`
+}
+
+type PublishedDocument struct {
+ FieldId string `json:"fileId"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Format string `json:"format"`
+ Title string `json:"title,omitempty"`
+ Labels []string `json:"labels,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Info interface{} `json:"info,omitempty"`
+ ExternalDocs interface{} `json:"externalDocs,omitempty"`
+ Operations []interface{} `json:"operations,omitempty"`
+ Filename string `json:"filename"`
+ Tags []interface{} `json:"tags"`
+}
+
+type PublishedDocumentRefView struct {
+ FieldId string `json:"fileId"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Format string `json:"format"`
+ Title string `json:"title,omitempty"`
+ Labels []string `json:"labels,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Filename string `json:"filename"`
+ PackageRef string `json:"packageRef"`
+ IncludedOperationIds []string `json:"includedOperationIds"`
+}
+
+type DocumentsForTransformationView struct {
+ Documents []DocumentForTransformationView `json:"documents"`
+}
+
+type DocumentForTransformationView struct {
+ FieldId string `json:"fileId"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Format string `json:"format"`
+ Title string `json:"title,omitempty"`
+ Labels []string `json:"labels,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Filename string `json:"filename"`
+ IncludedOperationIds []string `json:"includedOperationIds"`
+ Data []byte `json:"data"`
+}
+
+type Openapi struct {
+ Operations []OpenapiOperation `json:"operations,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Title string `json:"title"`
+}
+
+type OpenapiOperation struct {
+ Path string `json:"path"`
+ Method string `json:"method"`
+ Tile string `json:"tile"`
+ Tags []string `json:"tags"`
+}
+
+type Asyncapi struct {
+ Operations []AsyncapiOperation `json:"operations,omitempty"`
+ Description string `json:"description,omitempty"`
+ Version string `json:"version,omitempty"`
+ Title string `json:"title"`
+}
+
+type AsyncapiOperation struct {
+ Channel string `json:"channel"`
+ Method string `json:"method"`
+ Tags []string `json:"tags"`
+}
diff --git a/qubership-apihub-service/view/PublishedContentChange.go b/qubership-apihub-service/view/PublishedContentChange.go
new file mode 100644
index 0000000..d2873ff
--- /dev/null
+++ b/qubership-apihub-service/view/PublishedContentChange.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PublishedContentChange struct {
+ FileId string `json:"fileId"`
+ Type ShortcutType `json:"type"`
+ Title string `json:"title"`
+ Slug string `json:"slug"`
+ Checksum string `json:"-"`
+}
diff --git a/qubership-apihub-service/view/PublishedRef.go b/qubership-apihub-service/view/PublishedRef.go
new file mode 100644
index 0000000..f67f3c8
--- /dev/null
+++ b/qubership-apihub-service/view/PublishedRef.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type PublishedRef struct {
+ PackageId string `json:"refId"`
+ Kind string `json:"kind"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ VersionStatus string `json:"versionStatus"`
+ Alias string `json:"alias"`
+}
diff --git a/qubership-apihub-service/view/PublishedVersion.go b/qubership-apihub-service/view/PublishedVersion.go
new file mode 100644
index 0000000..0509c7c
--- /dev/null
+++ b/qubership-apihub-service/view/PublishedVersion.go
@@ -0,0 +1,77 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type PublishedVersion struct {
+ PackageId string `json:"-"`
+ Version string `json:"-"`
+ Status VersionStatus `json:"status"`
+ PublishedAt time.Time `json:"publishedAt"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ Changes Validation `json:"changes,omitempty"`
+ Validations ValidationsMap `json:"validations,omitempty"`
+ DeletedAt *time.Time `json:"-"`
+ RelatedPackages []PublishedRef `json:"refs"`
+ Contents []PublishedContent `json:"files"`
+ Revision int `json:"-"`
+ BranchName string `json:"-"`
+ VersionLabels []string `json:"versionLabels"`
+}
+
+type PublishedShortVersion struct {
+ PackageId string `json:"-"`
+ Version string `json:"-"`
+ Status VersionStatus `json:"status"`
+ PublishedAt time.Time `json:"publishedAt"`
+}
+
+type PublishedVersionListView_deprecated struct {
+ Version string `json:"version"`
+ Status VersionStatus `json:"status"`
+ PublishedAt time.Time `json:"publishedAt"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ Revision int `json:"revision"`
+}
+
+type PublishedVersions struct {
+ Versions []PublishedVersion `json:"versions"`
+}
+
+type PublishedVersionsView_deprecated struct {
+ Versions []PublishedVersionListView_deprecated `json:"versions"`
+}
+
+type PublishedVersionHistoryView struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ Status string `json:"status"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ PreviousVersion string `json:"previousVersion"`
+ PublishedAt time.Time `json:"publishedAt"`
+ ApiTypes []string `json:"apiTypes"`
+}
+
+type PublishedVersionHistoryFilter struct {
+ PublishedAfter *time.Time
+ PublishedBefore *time.Time
+ Status *string
+ Limit int
+ Page int
+}
diff --git a/qubership-apihub-service/view/Ref.go b/qubership-apihub-service/view/Ref.go
new file mode 100644
index 0000000..da218fa
--- /dev/null
+++ b/qubership-apihub-service/view/Ref.go
@@ -0,0 +1,52 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Ref struct {
+ RefPackageId string `json:"refId" msgpack:"refId"`
+ RefPackageName string `json:"name" msgpack:"name"`
+ RefPackageVersion string `json:"version" msgpack:"version"`
+ Status FileStatus `json:"status" msgpack:"status"`
+ VersionStatus string `json:"versionStatus" msgpack:"versionStatus"`
+ Kind string `json:"kind" msgpack:"kind"`
+ IsBroken bool `json:"isBroken" msgpack:"isBroken"` //TODO: Need to support all over the system
+}
+
+type RefGitConfigView struct {
+ RefPackageId string `json:"refId"`
+ Version string `json:"version"`
+}
+
+func TransformRefToGitView(ref Ref) RefGitConfigView {
+ return RefGitConfigView{
+ RefPackageId: ref.RefPackageId,
+ Version: ref.RefPackageVersion,
+ }
+}
+
+func TransformGitViewToRef(ref RefGitConfigView, refPackageName string, status string, kind string) Ref {
+ return Ref{
+ RefPackageId: ref.RefPackageId,
+ RefPackageName: refPackageName,
+ RefPackageVersion: ref.Version,
+ Status: StatusUnmodified,
+ VersionStatus: status,
+ Kind: kind,
+ }
+}
+
+func (r *Ref) EqualsGitView(r2 *Ref) bool {
+ return r.RefPackageId == r2.RefPackageId && r.RefPackageVersion == r2.RefPackageVersion
+}
diff --git a/qubership-apihub-service/view/RefChange.go b/qubership-apihub-service/view/RefChange.go
new file mode 100644
index 0000000..99393b0
--- /dev/null
+++ b/qubership-apihub-service/view/RefChange.go
@@ -0,0 +1,21 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type RefChange struct {
+ Before RefChangeView `json:"before"`
+ After RefChangeView `json:"after"`
+ Status FileStatus `json:"status"`
+}
diff --git a/qubership-apihub-service/view/RefChangeView.go b/qubership-apihub-service/view/RefChangeView.go
new file mode 100644
index 0000000..7f925eb
--- /dev/null
+++ b/qubership-apihub-service/view/RefChangeView.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+// todo maybe a better name?
+type RefChangeView struct {
+ RefProjectId string `json:"projectId"`
+ RefProjectName string `json:"name"`
+ RefProjectVersion string `json:"version"`
+ Status string `json:"status"`
+}
diff --git a/qubership-apihub-service/view/RefPatch.go b/qubership-apihub-service/view/RefPatch.go
new file mode 100644
index 0000000..7ea4039
--- /dev/null
+++ b/qubership-apihub-service/view/RefPatch.go
@@ -0,0 +1,27 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type RefPatch struct {
+ RefId string `json:"refId"`
+ Version string `json:"version"`
+ Status FileStatus `json:"status"`
+ Data RefPatchData `json:"data"`
+}
+
+type RefPatchData struct {
+ RefId string `json:"refId"`
+ Version string `json:"version"`
+}
diff --git a/qubership-apihub-service/view/RestOperation.go b/qubership-apihub-service/view/RestOperation.go
new file mode 100644
index 0000000..49c862c
--- /dev/null
+++ b/qubership-apihub-service/view/RestOperation.go
@@ -0,0 +1,64 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type RestOperationChange struct {
+ Path string `json:"path"`
+ Method string `json:"method"`
+ Tags []string `json:"tags,omitempty"`
+}
+
+type RestOperationMetadata struct {
+ Path string `json:"path"`
+ Method string `json:"method"`
+ Tags []string `json:"tags,omitempty"`
+}
+
+type RestOperationSingleView struct {
+ SingleOperationView
+ RestOperationMetadata
+}
+
+type RestOperationView struct {
+ OperationListView
+ RestOperationMetadata
+}
+
+type DeprecatedRestOperationView struct {
+ DeprecatedOperationView
+ RestOperationMetadata
+}
+
+type OperationSummary struct {
+ Endpoints int `json:"endpoints"`
+ Deprecated int `json:"deprecated"`
+ Created int `json:"created"`
+ Deleted int `json:"deleted"`
+}
+
+type RestOperationComparisonChangelogView_deprecated struct {
+ OperationComparisonChangelogView_deprecated
+ RestOperationChange
+}
+
+type RestOperationComparisonChangelogView struct {
+ OperationComparisonChangelogView
+ RestOperationChange
+}
+
+type RestOperationComparisonChangesView struct {
+ OperationComparisonChangesView
+ RestOperationChange
+}
diff --git a/qubership-apihub-service/view/Role.go b/qubership-apihub-service/view/Role.go
new file mode 100644
index 0000000..51337dc
--- /dev/null
+++ b/qubership-apihub-service/view/Role.go
@@ -0,0 +1,47 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+const SysadmRole = "System administrator"
+
+const AdminRoleId = "admin"
+const EditorRoleId = "editor"
+const ViewerRoleId = "viewer"
+const NoneRoleId = "none"
+
+type PackageRole struct {
+ RoleId string `json:"roleId"`
+ RoleName string `json:"role"`
+ ReadOnly bool `json:"readOnly,omitempty"`
+ Permissions []string `json:"permissions"`
+ Rank int `json:"rank"`
+}
+
+type PackageRoles struct {
+ Roles []PackageRole `json:"roles"`
+}
+
+type PackageRoleCreateReq struct {
+ Role string `json:"role" validate:"required"`
+ Permissions []string `json:"permissions" validate:"required"`
+}
+
+type PackageRoleUpdateReq struct {
+ Permissions *[]string `json:"permissions"`
+}
+
+type PackageRoleOrderReq struct {
+ Roles []string `json:"roles" validate:"required"`
+}
diff --git a/qubership-apihub-service/view/RolePermission.go b/qubership-apihub-service/view/RolePermission.go
new file mode 100644
index 0000000..f1a0425
--- /dev/null
+++ b/qubership-apihub-service/view/RolePermission.go
@@ -0,0 +1,102 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "fmt"
+
+type RolePermission string
+
+const (
+ ReadPermission RolePermission = "read"
+ CreateAndUpdatePackagePermission RolePermission = "create_and_update_package"
+ DeletePackagePermission RolePermission = "delete_package"
+ ManageDraftVersionPermission RolePermission = "manage_draft_version"
+ ManageReleaseVersionPermission RolePermission = "manage_release_version"
+ ManageArchivedVersionPermission RolePermission = "manage_archived_version"
+ UserAccessManagementPermission RolePermission = "user_access_management"
+ AccessTokenManagementPermission RolePermission = "access_token_management"
+)
+
+func GetAllRolePermissions() []RolePermission {
+ return []RolePermission{
+ ReadPermission,
+ CreateAndUpdatePackagePermission,
+ DeletePackagePermission,
+ ManageDraftVersionPermission,
+ ManageReleaseVersionPermission,
+ ManageArchivedVersionPermission,
+ UserAccessManagementPermission,
+ AccessTokenManagementPermission,
+ }
+}
+
+func (r RolePermission) Id() string {
+ return string(r)
+}
+
+func (r RolePermission) Name() string {
+ switch r {
+ case ReadPermission:
+ return "read content of public packages"
+ case CreateAndUpdatePackagePermission:
+ return "create, update group/package"
+ case DeletePackagePermission:
+ return "delete group/package"
+ case ManageDraftVersionPermission:
+ return "manage version in draft status"
+ case ManageReleaseVersionPermission:
+ return "manage version in release status"
+ case ManageArchivedVersionPermission:
+ return "manage version in archived status"
+ case UserAccessManagementPermission:
+ return "user access management"
+ case AccessTokenManagementPermission:
+ return "access token management"
+ default:
+ return ""
+ }
+}
+
+func ParseRolePermission(permissionId string) (RolePermission, error) {
+ switch permissionId {
+ case "read":
+ return ReadPermission, nil
+ case "create_and_update_package":
+ return CreateAndUpdatePackagePermission, nil
+ case "delete_package":
+ return DeletePackagePermission, nil
+ case "manage_draft_version":
+ return ManageDraftVersionPermission, nil
+ case "manage_release_version":
+ return ManageReleaseVersionPermission, nil
+ case "manage_archived_version":
+ return ManageArchivedVersionPermission, nil
+ case "user_access_management":
+ return UserAccessManagementPermission, nil
+ case "access_token_management":
+ return AccessTokenManagementPermission, nil
+ default:
+ return "", fmt.Errorf("permission '%v' doesn't exist", permissionId)
+ }
+}
+
+type Permission struct {
+ PermissionId string `json:"permission"`
+ Name string `json:"name"`
+}
+
+type Permissions struct {
+ Permissions []Permission `json:"permissions"`
+}
diff --git a/qubership-apihub-service/view/Search.go b/qubership-apihub-service/view/Search.go
new file mode 100644
index 0000000..ac7095c
--- /dev/null
+++ b/qubership-apihub-service/view/Search.go
@@ -0,0 +1,189 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+const SearchLevelOperations = "operations"
+const SearchLevelPackages = "packages"
+const SearchLevelDocuments = "documents"
+
+const ScopeAll = "all"
+
+const RestScopeRequest = "request"
+const RestScopeResponse = "response"
+const RestScopeAnnotation = "annotation"
+const RestScopeExamples = "examples"
+const RestScopeProperties = "properties"
+
+const GraphqlScopeAnnotation = "annotation"
+const GraphqlScopeArgument = "argument"
+const GraphqlScopeProperty = "property"
+
+func ValidRestOperationScope(scope string) bool {
+ switch scope {
+ case ScopeAll, RestScopeRequest, RestScopeResponse, RestScopeAnnotation, RestScopeExamples, RestScopeProperties:
+ return true
+ }
+ return false
+}
+
+func ValidGraphqlOperationScope(scope string) bool {
+ switch scope {
+ case ScopeAll, GraphqlScopeAnnotation, GraphqlScopeArgument, GraphqlScopeProperty:
+ return true
+ }
+ return false
+}
+
+type PublicationDateInterval struct {
+ StartDate time.Time `json:"startDate"`
+ EndDate time.Time `json:"endDate"`
+}
+
+type OperationSearchParams struct {
+ ApiType string `json:"apiType"`
+ Scopes []string `json:"scope"`
+ DetailedScopes []string `json:"detailedScope"`
+ Methods []string `json:"methods"`
+ OperationTypes []string `json:"operationTypes"`
+}
+
+type SearchQueryReq struct {
+ SearchString string `json:"searchString" validate:"required"`
+ PackageIds []string `json:"packageIds"`
+ Versions []string `json:"versions"`
+ Statuses []string `json:"statuses"`
+ PublicationDateInterval PublicationDateInterval `json:"creationDateInterval"`
+ OperationSearchParams *OperationSearchParams `json:"operationParams"`
+ Limit int `json:"-"`
+ Page int `json:"-"`
+}
+
+// deprecated
+type SearchResult_deprecated struct {
+ Operations *[]OperationSearchResult_deprecated `json:"operations,omitempty"`
+ Packages *[]PackageSearchResult `json:"packages,omitempty"`
+ Documents *[]DocumentSearchResult `json:"documents,omitempty"`
+}
+
+type SearchResult struct {
+ Operations *[]interface{} `json:"operations,omitempty"`
+ Packages *[]PackageSearchResult `json:"packages,omitempty"`
+ Documents *[]DocumentSearchResult `json:"documents,omitempty"`
+}
+
+type OperationSearchWeightsDebug struct {
+ ScopeWeight float64 `json:"scopeWeight"`
+ ScopeTf float64 `json:"scopeTf"`
+ TitleTf float64 `json:"titleTf"`
+ VersionStatusTf float64 `json:"versionStatusTf"`
+ OperationOpenCountWeight float64 `json:"operationOpenCountWeight"`
+ OperationOpenCount float64 `json:"operationOpenCount"`
+}
+
+// deprecated
+type OperationSearchResult_deprecated struct {
+ PackageId string `json:"packageId"`
+ PackageName string `json:"name"`
+ ParentPackages []string `json:"parentPackages"`
+ Version string `json:"version"`
+ VersionStatus string `json:"status"`
+ OperationId string `json:"operationId"`
+ Title string `json:"title"`
+ Deprecated bool `json:"deprecated,omitempty"`
+ ApiType string `json:"apiType"`
+ Metadata interface{} `json:"metadata"`
+
+ //debug
+ Debug OperationSearchWeightsDebug `json:"debug,omitempty"`
+}
+
+type CommonOperationSearchResult struct {
+ PackageId string `json:"packageId"`
+ PackageName string `json:"name"`
+ ParentPackages []string `json:"parentPackages"`
+ VersionStatus string `json:"status"`
+ Version string `json:"version"`
+ Title string `json:"title"`
+
+ //debug
+ Debug OperationSearchWeightsDebug `json:"debug,omitempty"`
+}
+
+type RestOperationSearchResult struct {
+ RestOperationView
+ CommonOperationSearchResult
+}
+
+type GraphQLOperationSearchResult struct {
+ GraphQLOperationView
+ CommonOperationSearchResult
+}
+
+type PackageSearchWeightsDebug struct {
+ PackageIdTf float64 `json:"packageIdTf"`
+ PackageNameTf float64 `json:"packageNameTf"`
+ PackageDescriptionTf float64 `json:"packageDescriptionTf"`
+ PackageServiceNameTf float64 `json:"packageServiceNameTf"`
+ VersionTf float64 `json:"versionTf"`
+ VersionLabelsTf float64 `json:"versionLabelsTf"`
+ DefaultVersionTf float64 `json:"defaultVersionTf"`
+ VersionStatusTf float64 `json:"versionStatusTf"`
+ VersionOpenCountWeight float64 `json:"versionOpenCountWeight"`
+ VersionOpenCount float64 `json:"versionOpenCount"`
+}
+
+type PackageSearchResult struct {
+ PackageId string `json:"packageId"`
+ PackageName string `json:"name"`
+ Description string `json:"description,omitempty"`
+ ServiceName string `json:"serviceName,omitempty"`
+ ParentPackages []string `json:"parentPackages"`
+ Version string `json:"version"`
+ VersionStatus string `json:"status"`
+ CreatedAt time.Time `json:"createdAt"`
+ Labels []string `json:"labels,omitempty"`
+ LatestRevision bool `json:"latestRevision,omitempty"`
+
+ //debug
+ Debug PackageSearchWeightsDebug `json:"debug,omitempty"`
+}
+
+type DocumentSearchWeightsDebug struct {
+ TitleTf float64 `json:"titleTf"`
+ LabelsTf float64 `json:"labelsTf"`
+ ContentTf float64 `json:"contentTf"`
+ VersionStatusTf float64 `json:"versionStatusTf"`
+ DocumentOpenCountWeight float64 `json:"documentOpenCountWeight"`
+ DocumentOpenCount float64 `json:"documentOpenCount"`
+}
+
+type DocumentSearchResult struct {
+ PackageId string `json:"packageId"`
+ PackageName string `json:"name"`
+ ParentPackages []string `json:"parentPackages"`
+ Version string `json:"version"`
+ VersionStatus string `json:"status"`
+ CreatedAt time.Time `json:"createdAt"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Title string `json:"title"`
+ Labels []string `json:"labels,omitempty"`
+ Content string `json:"content,omitempty"`
+
+ //debug
+ Debug DocumentSearchWeightsDebug `json:"debug,omitempty"`
+}
diff --git a/qubership-apihub-service/view/Services.go b/qubership-apihub-service/view/Services.go
new file mode 100644
index 0000000..9cab1fd
--- /dev/null
+++ b/qubership-apihub-service/view/Services.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ServiceNameItem struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+}
+
+type ServiceNamesResponse struct {
+ ServiceNames []ServiceNameItem `json:"serviceNames"`
+}
diff --git a/qubership-apihub-service/view/ShortcutType.go b/qubership-apihub-service/view/ShortcutType.go
new file mode 100644
index 0000000..a1219ac
--- /dev/null
+++ b/qubership-apihub-service/view/ShortcutType.go
@@ -0,0 +1,76 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ShortcutType string
+
+// todo maybe add plain text type
+const (
+ OpenAPI31 ShortcutType = "openapi-3-1"
+ OpenAPI30 ShortcutType = "openapi-3-0"
+ OpenAPI20 ShortcutType = "openapi-2-0"
+ AsyncAPI ShortcutType = "asyncapi-2"
+ JsonSchema ShortcutType = "json-schema"
+ MD ShortcutType = "markdown"
+ GraphQLSchema ShortcutType = "graphql-schema"
+ GraphAPI ShortcutType = "graphapi"
+ Introspection ShortcutType = "introspection"
+ Unknown ShortcutType = "unknown"
+)
+
+func (s ShortcutType) String() string {
+ return string(s)
+}
+
+func ParseTypeFromString(s string) ShortcutType {
+ switch s {
+ case "openapi-3-0":
+ return OpenAPI30
+ case "openapi-3-1":
+ return OpenAPI31
+ case "openapi-2-0":
+ return OpenAPI20
+ case "asyncapi-2":
+ return AsyncAPI
+ case "markdown":
+ return MD
+ case "unknown":
+ return Unknown
+ case "json-schema":
+ return JsonSchema
+ case "graphql-schema":
+ return GraphQLSchema
+ case "graphapi":
+ return GraphAPI
+ case "introspection":
+ return Introspection
+ default:
+ return Unknown
+ }
+}
+
+func ComparableTypes(type1 ShortcutType, type2 ShortcutType) bool {
+ if type1 == type2 {
+ return true
+ }
+ if type1 == OpenAPI30 && type2 == OpenAPI31 {
+ return true
+ }
+ if type1 == OpenAPI31 && type2 == OpenAPI30 {
+ return true
+ }
+
+ return false
+}
diff --git a/qubership-apihub-service/view/Status.go b/qubership-apihub-service/view/Status.go
new file mode 100644
index 0000000..0d81ec3
--- /dev/null
+++ b/qubership-apihub-service/view/Status.go
@@ -0,0 +1,23 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Status struct {
+ Status string `json:"status"`
+}
+
+type Statuses struct {
+ Statuses []string `json:"statuses"`
+}
diff --git a/qubership-apihub-service/view/SystemInfo.go b/qubership-apihub-service/view/SystemInfo.go
new file mode 100644
index 0000000..2a1a76b
--- /dev/null
+++ b/qubership-apihub-service/view/SystemInfo.go
@@ -0,0 +1,29 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type SystemInfo struct {
+ BackendVersion string `json:"backendVersion"`
+ FrontendVersion string `json:"frontendVersion"`
+ ProductionMode bool `json:"productionMode"`
+ Notification string `json:"notification,omitempty"`
+ ExternalLinks []string `json:"externalLinks"`
+}
+
+type SystemConfigurationInfo struct {
+ SSOIntegrationEnabled bool `json:"ssoIntegrationEnabled"`
+ AutoRedirect bool `json:"autoRedirect"`
+ DefaultWorkspaceId string `json:"defaultWorkspaceId"`
+}
diff --git a/qubership-apihub-service/view/Transition.go b/qubership-apihub-service/view/Transition.go
new file mode 100644
index 0000000..b5aedb5
--- /dev/null
+++ b/qubership-apihub-service/view/Transition.go
@@ -0,0 +1,44 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "time"
+
+type TransitionRequest struct {
+ From string `json:"from" validate:"required"`
+ To string `json:"to" validate:"required"`
+
+ OverwriteHistory bool `json:"overwriteHistory"`
+}
+
+type TransitionStatus struct {
+ Id string `json:"id"`
+ TrType string `json:"trType"`
+ FromId string `json:"fromId"`
+ ToId string `json:"toId"`
+ Status string `json:"status"`
+ Details string `json:"details,omitempty"`
+ StartedBy string `json:"startedBy"`
+ StartedAt time.Time `json:"startedAt"`
+ FinishedAt time.Time `json:"finishedAt"`
+ ProgressPercent int `json:"progressPercent"`
+ AffectedObjects int `json:"affectedObjects"`
+ CompletedSerialNumber *int `json:"completedSerialNumber"`
+}
+
+type PackageTransition struct {
+ OldPackageId string `json:"oldPackageId"`
+ NewPackageId string `json:"newPackageId"`
+}
diff --git a/qubership-apihub-service/view/User.go b/qubership-apihub-service/view/User.go
new file mode 100644
index 0000000..720b0b0
--- /dev/null
+++ b/qubership-apihub-service/view/User.go
@@ -0,0 +1,62 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type User struct {
+ Id string `json:"id"`
+ Email string `json:"email"`
+ Name string `json:"name"`
+ AvatarUrl string `json:"avatarUrl"`
+}
+
+type UserAvatar struct {
+ Id string
+ Avatar []byte
+ Checksum [32]byte
+}
+
+type Users struct {
+ Users []User `json:"users"`
+}
+
+type LdapUsers struct {
+ Users []LdapUser
+}
+
+type LdapUser struct {
+ Id string
+ Email string
+ Name string
+ Avatar []byte
+}
+
+type UsersListReq struct {
+ Filter string `json:"filter"`
+ Limit int `json:"limit"`
+ Page int `json:"page"`
+}
+
+type InternalUser struct {
+ Id string `json:"-"`
+ Email string `json:"email" validate:"required"`
+ Name string `json:"name"`
+ Password string `json:"password" validate:"required"`
+ PrivateWorkspaceId string `json:"privateWorkspaceId"`
+}
+
+type LdapSearchFilterReq struct {
+ FilterToValue map[string]string
+ Limit int
+}
diff --git a/qubership-apihub-service/view/Validation.go b/qubership-apihub-service/view/Validation.go
new file mode 100644
index 0000000..b380c26
--- /dev/null
+++ b/qubership-apihub-service/view/Validation.go
@@ -0,0 +1,102 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type Validation struct {
+ Summary interface{} `json:"summary,omitempty"`
+ //Data []interface{} `json:"data,omitempty"`
+}
+
+type ValidationsMap map[string]Validation
+
+type VersionValidationChanges struct {
+ PreviousVersion string `json:"previousVersion,omitempty"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ Changes []VersionChangelogData `json:"changes"`
+ Bwc []VersionBwcData `json:"bwcMessages"`
+}
+
+type VersionValidationProblems struct {
+ Spectral []VersionSpectralData `json:"messages"`
+}
+
+// changelog.json
+type VersionChangelog struct {
+ Summary VersionChangelogSummary `json:"summary,omitempty"`
+ Data []VersionChangelogData `json:"data,omitempty"`
+}
+
+type VersionChangelogSummary struct {
+ Breaking int `json:"breaking"`
+ NonBreaking int `json:"non-breaking"`
+ Unclassified int `json:"unclassified"`
+ SemiBreaking int `json:"semi-breaking"`
+ Annotation int `json:"annotation"`
+ Deprecate int `json:"deprecate"`
+}
+
+type VersionChangelogData struct {
+ FileId string `json:"fileId,omitempty"`
+ Slug string `json:"slug,omitempty"`
+ PreviousFileId string `json:"previousFileId,omitempty"`
+ PreviousSlug string `json:"previousSlug,omitempty"`
+ Openapi *OpenapiOperation `json:"openapi,omitempty"`
+ Asyncapi *AsyncapiOperation `json:"asyncapi,omitempty"`
+ JsonPath []string `json:"jsonPath,omitempty" validate:"required"`
+ Action string `json:"action,omitempty" validate:"required"`
+ Severity string `json:"severity,omitempty" validate:"required"`
+}
+
+// spectral.json
+type VersionSpectral struct {
+ Summary VersionSpectralSummary `json:"summary,omitempty"`
+ Data []VersionSpectralData `json:"data,omitempty"`
+}
+
+type VersionSpectralSummary struct {
+ Errors int `json:"error"`
+ Warnings int `json:"warnings"`
+}
+
+type VersionSpectralData struct {
+ FileId string `json:"fileId,omitempty"`
+ Slug string `json:"slug,omitempty"`
+ JsonPath []string `json:"jsonPath,omitempty"`
+ ExternalFilePath string `json:"externalFilePath,omitempty"`
+ Message string `json:"message" validate:"required"`
+ Severity int `json:"severity" validate:"required"`
+}
+
+// bwc.json
+type VersionBwc struct {
+ Summary VersionBwcSummary `json:"summary,omitempty"`
+ Data []VersionBwcData `json:"data,omitempty"`
+}
+
+type VersionBwcSummary struct {
+ Errors int `json:"error"`
+ Warnings int `json:"warnings"`
+}
+
+type VersionBwcData struct {
+ FileId string `json:"fileId,omitempty"`
+ PreviousFileId string `json:"previousFileId,omitempty"`
+ Slug string `json:"slug,omitempty"`
+ PreviousSlug string `json:"previousSlug,omitempty"`
+ JsonPath []string `json:"jsonPath,omitempty"`
+ ExternalFilePath string `json:"externalFilePath,omitempty"`
+ Message string `json:"message" validate:"required"`
+ Severity int `json:"severity" validate:"required"`
+}
diff --git a/qubership-apihub-service/view/ValidationReport.go b/qubership-apihub-service/view/ValidationReport.go
new file mode 100644
index 0000000..1ac1460
--- /dev/null
+++ b/qubership-apihub-service/view/ValidationReport.go
@@ -0,0 +1,120 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type ValidationStatus string
+type ContentValidationType string
+
+const (
+ RvStatusYes ValidationStatus = "YES"
+ RvStatusSemi ValidationStatus = "SEMI"
+ RvStatusNo ValidationStatus = "NO"
+ RvStatusInProgress ValidationStatus = "IN_PROGRESS"
+)
+
+const (
+ CvTypeError ContentValidationType = "ERROR"
+ CvTypeWarning ContentValidationType = "WARNING"
+ CvTypeInformation ContentValidationType = "INFORMATION"
+ CvTypeRecommendation ContentValidationType = "RECOMMENDATION"
+)
+
+type ValidationReport struct {
+ ValidationId string `json:"validationId"`
+ GroupName string `json:"groupName"`
+ Status ValidationStatus `json:"status"`
+ Error string `json:"error,omitempty"`
+ Packages []PackageValidationReport `json:"packages,omitempty"`
+}
+
+type PackageValidationReport struct {
+ PackageId string `json:"packageId"`
+ PackageName string `json:"packageName"`
+ Status ValidationStatus `json:"status"`
+ Files []FileValidationReport `json:"files,omitempty"`
+}
+
+type FileValidationReport struct {
+ Slug string `json:"slug"`
+ Status ValidationStatus `json:"status"`
+ FileMessages []FileValidationMessage `json:"messages,omitempty"`
+}
+
+type FileValidationMessage struct {
+ Type ContentValidationType `json:"type"`
+ Path string `json:"path"`
+ Text string `json:"text"`
+}
+
+type FileDataInfo struct {
+ Checksum string
+ Data []byte
+ Format string
+}
+
+func (f ValidationStatus) String() string {
+ switch f {
+ case RvStatusYes:
+ return "OK"
+ case RvStatusNo:
+ return "NO"
+ case RvStatusSemi:
+ return "SEMI"
+ case RvStatusInProgress:
+ return "IN-PROGRESS"
+ default:
+ return "NONE"
+ }
+}
+
+func (f ContentValidationType) String() string {
+ switch f {
+ case CvTypeError:
+ return "ERROR"
+ case CvTypeWarning:
+ return "WARNING"
+ case CvTypeInformation:
+ return "INFORMATION"
+ default:
+ return "RECOMMENDATION"
+ }
+}
+func ParseCvTypeInt(severity int) ContentValidationType {
+ switch severity {
+ case 0:
+ return CvTypeError
+ case 1:
+ return CvTypeWarning
+ case 2:
+ return CvTypeInformation
+ default:
+ return CvTypeRecommendation
+ }
+}
+
+func (f ContentValidationType) ToInt() int {
+ switch f {
+ case CvTypeError:
+ return 0
+ case CvTypeWarning:
+ return 1
+ case CvTypeInformation:
+ return 2
+ case CvTypeRecommendation:
+ return 3
+ default:
+ return 4
+ }
+}
diff --git a/qubership-apihub-service/view/Version.go b/qubership-apihub-service/view/Version.go
new file mode 100644
index 0000000..04089ad
--- /dev/null
+++ b/qubership-apihub-service/view/Version.go
@@ -0,0 +1,267 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import (
+ "time"
+)
+
+type VersionContent_deprecated struct {
+ PublishedAt time.Time `json:"createdAt"`
+ PublishedBy string `json:"createdBy"`
+ PreviousVersion string `json:"previousVersion,omitempty"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ VersionLabels []string `json:"versionLabels,omitempty"`
+ Status string `json:"status"`
+ OperationTypes []VersionOperationType `json:"operationTypes,omitempty"`
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+ RevisionsCount int `json:"revisionsCount,omitempty"`
+ OperationGroups []VersionOperationGroup `json:"operationGroups,omitempty"`
+}
+type VersionContent struct {
+ PublishedAt time.Time `json:"createdAt"`
+ PublishedBy map[string]interface{} `json:"createdBy"`
+ PreviousVersion string `json:"previousVersion,omitempty"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ VersionLabels []string `json:"versionLabels,omitempty"`
+ Status string `json:"status"`
+ OperationTypes []VersionOperationType `json:"operationTypes,omitempty"`
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+ RevisionsCount int `json:"revisionsCount,omitempty"`
+ OperationGroups []VersionOperationGroup `json:"operationGroups,omitempty"`
+}
+
+type VersionOperationType struct {
+ ApiType string `json:"apiType"`
+ OperationsCount *int `json:"operationsCount,omitempty"`
+ DeprecatedCount *int `json:"deprecatedCount,omitempty"`
+ NoBwcOperationsCount *int `json:"noBwcOperationsCount,omitempty"`
+ ChangesSummary *ChangeSummary `json:"changesSummary,omitempty"`
+ NumberOfImpactedOperations *ChangeSummary `json:"numberOfImpactedOperations,omitempty"`
+ InternalAudienceOperationsCount *int `json:"internalAudienceOperationsCount,omitempty"`
+ UnknownAudienceOperationsCount *int `json:"unknownAudienceOperationsCount,omitempty"`
+ ApiAudienceTransitions []ApiAudienceTransition `json:"apiAudienceTransitions,omitempty"`
+ Operations map[string]string `json:"operations,omitempty"`
+}
+
+type VersionOperationGroup struct {
+ GroupName string `json:"groupName"`
+ ApiType string `json:"apiType"`
+ Description string `json:"description,omitempty"`
+ IsPrefixGroup bool `json:"isPrefixGroup"`
+ OperationsCount int `json:"operationsCount"`
+ GhostOperationsCount int `json:"ghostOperationsCount,omitempty"`
+ ExportTemplateFilename string `json:"exportTemplateFileName,omitempty"`
+}
+
+type VersionDocuments struct {
+ Documents []PublishedDocumentRefView `json:"documents"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+// deprecated
+type VersionReferences struct {
+ References []VersionReference `json:"references"`
+}
+
+// deprecated
+type VersionReference struct {
+ RefId string `json:"refId"`
+ Kind string `json:"kind"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ Status string `json:"status"`
+ DeletedAt *time.Time `json:"deletedAt,omitempty"`
+ DeletedBy string `json:"deletedBy,omitempty"`
+ Parents []ParentPackageInfo `json:"parents"`
+}
+
+type VersionReferencesV3 struct {
+ References []VersionReferenceV3 `json:"references"`
+ Packages map[string]PackageVersionRef `json:"packages,omitempty"`
+}
+
+type VersionReferenceV3 struct {
+ PackageRef string `json:"packageRef"`
+ ParentPackageRef string `json:"parentPackageRef,omitempty"`
+ Excluded bool `json:"excluded,omitempty"`
+}
+
+type File struct {
+ FieldId string `json:"fieldId"`
+ Slug string `json:"slug"`
+ Type string `json:"type"`
+ Format string `json:"format"`
+ Title string `json:"title"`
+ Labels []string `json:"labels"`
+}
+
+type PublishedVersionListView_deprecated_v2 struct {
+ Version string `json:"version"`
+ Status string `json:"status"`
+ CreatedAt time.Time `json:"createdAt"`
+ CreatedBy string `json:"createdBy"`
+ VersionLabels []string `json:"versionLabels"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+}
+
+type PublishedVersionListView struct {
+ Version string `json:"version"`
+ Status string `json:"status"`
+ CreatedAt time.Time `json:"createdAt"`
+ CreatedBy map[string]interface{} `json:"createdBy"`
+ VersionLabels []string `json:"versionLabels"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId,omitempty"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+}
+
+type PublishedVersionsView_deprecated_v2 struct {
+ Versions []PublishedVersionListView_deprecated_v2 `json:"versions"`
+}
+type PublishedVersionsView struct {
+ Versions []PublishedVersionListView `json:"versions"`
+}
+
+type SharedUrlResult struct {
+ SharedFileId string `json:"sharedFileId"`
+}
+
+type SharedFilesReq struct {
+ PackageId string `json:"packageId"`
+ Version string `json:"version"`
+ Slug string `json:"slug"`
+}
+
+type VersionPatchRequest struct {
+ Status *string `json:"status"`
+ VersionLabels *[]string `json:"versionLabels"`
+}
+
+type VersionListReq struct {
+ PackageId string
+ Status string
+ Limit int
+ Page int
+ TextFilter string
+ Label string
+ CheckRevisions bool
+ SortBy string
+ SortOrder string
+}
+type VersionReferencesReq struct {
+ Limit int
+ Page int
+ TextFilter string
+ Kind string
+ ShowAllDescendants bool
+}
+
+type CompareVersionsReq struct {
+ PackageId string `json:"packageId" validate:"required"`
+ Version string `json:"version" validate:"required"`
+ PreviousVersion string `json:"previousVersion" validate:"required"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId" validate:"required"`
+}
+
+type PackageVersionRef struct {
+ RefPackageId string `json:"refId"`
+ Kind string `json:"kind"`
+ RefPackageName string `json:"name"`
+ RefPackageVersion string `json:"version"`
+ Status string `json:"status"`
+ DeletedAt *time.Time `json:"deletedAt,omitempty"`
+ DeletedBy string `json:"deletedBy,omitempty"`
+ ParentNames []string `json:"parentPackages,omitempty"`
+ ServiceName string `json:"-"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+}
+
+type PackageVersionRevisions_deprecated struct {
+ Revisions []PackageVersionRevision_deprecated `json:"revisions"`
+}
+type PackageVersionRevisions struct {
+ Revisions []PackageVersionRevision `json:"revisions"`
+}
+type PackageVersionRevision_deprecated struct {
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ Status string `json:"status"`
+ CreatedBy User `json:"createdBy"`
+ CreatedAt time.Time `json:"createdAt"`
+ RevisionLabels []string `json:"revisionLabels"`
+ PublishMeta BuildConfigMetadata `json:"publishMeta"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+}
+type PackageVersionRevision struct {
+ Version string `json:"version"`
+ Revision int `json:"revision"`
+ Status string `json:"status"`
+ CreatedBy map[string]interface{} `json:"createdBy"`
+ CreatedAt time.Time `json:"createdAt"`
+ RevisionLabels []string `json:"revisionLabels"`
+ PublishMeta BuildConfigMetadata `json:"publishMeta"`
+ NotLatestRevision bool `json:"notLatestRevision,omitempty"`
+}
+
+type DeleteVersionsRecursivelyReq struct {
+ OlderThanDate time.Time `json:"olderThanDate"`
+}
+
+type CopyVersionReq struct {
+ TargetPackageId string `json:"targetPackageId" validate:"required"`
+ TargetVersion string `json:"targetVersion" validate:"required"`
+ TargetPreviousVersion string `json:"targetPreviousVersion"`
+ TargetPreviousVersionPackageId string `json:"targetPreviousVersionPackageId"`
+ TargetStatus string `json:"targetStatus" validate:"required"`
+ TargetVersionLabels []string `json:"targetVersionLabels"`
+}
+
+type CopyVersionResp struct {
+ PublishId string `json:"publishId"`
+}
+
+const VersionSortOrderAsc = "asc"
+const VersionSortOrderDesc = "desc"
+
+const VersionSortByVersion = "version"
+const VersionSortByCreatedAt = "createdAt"
+
+type PublishFromCSVReq struct {
+ PackageId string `json:"packageId" validate:"required"`
+ Version string `json:"version" validate:"required"`
+ PreviousVersion string `json:"previousVersion"`
+ PreviousVersionPackageId string `json:"previousVersionPackageId"`
+ Status string `json:"status" validate:"required"`
+ VersionLabels []string `json:"versionLabels"`
+ CSVData []byte `json:"csvData"`
+ ServicesWorkspaceId string `json:"servicesWorkspaceId" validate:"required"` //workspace for matching packages by serviceNames
+}
+
+type PublishFromCSVResp struct {
+ PublishId string `json:"publishId"`
+}
+
+type CSVDashboardPublishStatusResponse struct {
+ Status string `json:"status"`
+ Message string `json:"message"`
+}
diff --git a/qubership-apihub-service/view/VersionChange.go b/qubership-apihub-service/view/VersionChange.go
new file mode 100644
index 0000000..fc07c40
--- /dev/null
+++ b/qubership-apihub-service/view/VersionChange.go
@@ -0,0 +1,20 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+type VersionChange struct {
+ Files []ContentChange `json:"files"`
+ Refs []RefChange `json:"refs"`
+}
diff --git a/qubership-apihub-service/view/VersionStatus.go b/qubership-apihub-service/view/VersionStatus.go
new file mode 100644
index 0000000..bfd7851
--- /dev/null
+++ b/qubership-apihub-service/view/VersionStatus.go
@@ -0,0 +1,50 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view
+
+import "fmt"
+
+type VersionStatus string
+
+const (
+ Draft VersionStatus = "draft"
+ Release VersionStatus = "release"
+ Archived VersionStatus = "archived"
+)
+
+func (v VersionStatus) String() string {
+ switch v {
+ case Draft:
+ return "draft"
+ case Release:
+ return "release"
+ case Archived:
+ return "archived"
+ default:
+ return ""
+ }
+}
+
+func ParseVersionStatus(s string) (VersionStatus, error) {
+ switch s {
+ case "draft":
+ return Draft, nil
+ case "release":
+ return Release, nil
+ case "archived":
+ return Archived, nil
+ }
+ return "", fmt.Errorf("unknown version status: %v", s)
+}
diff --git a/qubership-apihub-service/websocket/Handlers.go b/qubership-apihub-service/websocket/Handlers.go
new file mode 100644
index 0000000..c68d121
--- /dev/null
+++ b/qubership-apihub-service/websocket/Handlers.go
@@ -0,0 +1,24 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+type WsMessageHandler interface {
+ HandleMessage(data []byte, wsId string, session *WsEditSession)
+}
+
+type SessionClosedHandler interface {
+ HandleSessionClosed(editSessionId string)
+ HandleUserDisconnected(editSessionId string, wsId string)
+}
diff --git a/qubership-apihub-service/websocket/RemoteEvents.go b/qubership-apihub-service/websocket/RemoteEvents.go
new file mode 100644
index 0000000..416259a
--- /dev/null
+++ b/qubership-apihub-service/websocket/RemoteEvents.go
@@ -0,0 +1,68 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+type BranchEvent struct {
+ ProjectId string `json:"projectId" msgpack:"projectId"`
+ BranchName string `json:"branchName" msgpack:"branchName"`
+ WsId string `json:"wsId" msgpack:"wsId"`
+ Action interface{} `json:"action" msgpack:"action"`
+}
+
+func BranchEventFromMap(m map[string]interface{}) BranchEvent {
+ return BranchEvent{
+ ProjectId: m["projectId"].(string),
+ BranchName: m["branchName"].(string),
+ WsId: m["wsId"].(string),
+ Action: m["action"],
+ }
+}
+
+func BranchEventToMap(e BranchEvent) map[string]interface{} {
+ result := map[string]interface{}{}
+ result["projectId"] = e.ProjectId
+ result["branchName"] = e.BranchName
+ result["wsId"] = e.WsId
+ result["action"] = e.Action
+ return result
+}
+
+type FileEvent struct {
+ ProjectId string `json:"projectId" msgpack:"projectId"`
+ BranchName string `json:"branchName" msgpack:"branchName"`
+ FileId string `json:"fileId" msgpack:"fileId"`
+ Action string `json:"action" msgpack:"action"`
+ Content string `json:"content" msgpack:"content"`
+}
+
+func FileEventFromMap(m map[string]interface{}) FileEvent {
+ return FileEvent{
+ ProjectId: m["projectId"].(string),
+ BranchName: m["branchName"].(string),
+ FileId: m["fileId"].(string),
+ Action: m["action"].(string),
+ Content: m["content"].(string),
+ }
+}
+
+func FileEventToMap(e FileEvent) map[string]interface{} {
+ result := map[string]interface{}{}
+ result["projectId"] = e.ProjectId
+ result["branchName"] = e.BranchName
+ result["fileId"] = e.FileId
+ result["action"] = e.Action
+ result["content"] = e.Content
+ return result
+}
diff --git a/qubership-apihub-service/websocket/WsBranchActions.go b/qubership-apihub-service/websocket/WsBranchActions.go
new file mode 100644
index 0000000..214c978
--- /dev/null
+++ b/qubership-apihub-service/websocket/WsBranchActions.go
@@ -0,0 +1,125 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+import (
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/view"
+)
+
+const (
+ BranchConfigSnapshotType = "branch:config:snapshot"
+ BranchConfigUpdatedType = "branch:config:updated"
+ BranchFilesUpdatedType = "branch:files:updated"
+ BranchFilesResetType = "branch:files:reset"
+ BranchFilesDataModifiedType = "branch:files:data:modified"
+ BranchRefsUpdatedType = "branch:refs:updated"
+ BranchSavedType = "branch:saved"
+ BranchResetType = "branch:reset"
+ BranchEditorAddedType = "branch:editors:added"
+ BranchEditorRemovedType = "branch:editors:removed"
+)
+
+type BranchConfigSnapshot struct {
+ Type string `json:"type" msgpack:"type"`
+ Data interface{} `json:"data" msgpack:"data"`
+}
+
+type BranchConfigUpdatedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ Data BranchConfigUpdatedPatchData `json:"data" msgpack:"data"`
+}
+
+type BranchConfigUpdatedPatchData struct {
+ ChangeType view.ChangeType `json:"changeType" msgpack:"changeType"`
+}
+
+type BranchFilesUpdatedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ Operation string `json:"operation" msgpack:"operation"`
+ FileId string `json:"fileId,omitempty" msgpack:"fileId,omitempty"`
+ Data *BranchFilesUpdatedPatchData `json:"data,omitempty" msgpack:"data,omitempty"`
+}
+
+type BranchResetPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+}
+
+type BranchFilesResetPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ FileId string `json:"fileId" msgpack:"fileId"`
+}
+
+type BranchFilesUpdatedPatchData struct {
+ FileId string `json:"fileId,omitempty" msgpack:"fileId,omitempty"`
+ Publish *bool `json:"publish,omitempty" msgpack:"publish,omitempty"`
+ Labels *[]string `json:"labels,omitempty" msgpack:"labels,omitempty"`
+ Status view.FileStatus `json:"status,omitempty" msgpack:"status,omitempty"`
+ MovedFrom *string `json:"movedFrom,omitempty" msgpack:"movedFrom,omitempty"`
+ ChangeType view.ChangeType `json:"changeType,omitempty" msgpack:"changeType,omitempty"`
+ BlobId *string `json:"blobId,omitempty" msgpack:"blobId,omitempty"`
+ ConflictedBlobId *string `json:"conflictedBlobId,omitempty" msgpack:"conflictedBlobId,omitempty"`
+ ConflictedFileId *string `json:"conflictedFileId,omitempty" msgpack:"conflictedFileId,omitempty"`
+}
+
+type BranchFilesDataModified struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ FileId string `json:"fileId" msgpack:"fileId"`
+}
+
+type BranchRefsUpdatedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ Operation string `json:"operation" msgpack:"operation"`
+ RefId string `json:"refId,omitempty" msgpack:"refId,omitempty"`
+ Version string `json:"version,omitempty" msgpack:"version,omitempty"`
+ Data *BranchRefsUpdatedPatchData `json:"data,omitempty" msgpack:"data,omitempty"`
+}
+
+type BranchRefsUpdatedPatchData struct {
+ RefId string `json:"refId,omitempty" msgpack:"refId,omitempty"`
+ Version string `json:"version,omitempty" msgpack:"version,omitempty"`
+ Name string `json:"name,omitempty" msgpack:"name,omitempty"`
+ VersionStatus string `json:"versionStatus,omitempty" msgpack:"versionStatus,omitempty"`
+ Status view.FileStatus `json:"status,omitempty" msgpack:"status,omitempty"`
+}
+
+type BranchSavedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ Comment string `json:"comment" msgpack:"comment"`
+ Branch string `json:"branch,omitempty" msgpack:",omitempty"`
+ MergeRequestURL *string `json:"mrUrl,omitempty" msgpack:",omitempty"`
+}
+
+type BranchPublishedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+ Version string `json:"version" msgpack:"version"`
+ Status string `json:"status" msgpack:"status"`
+}
+
+type BranchEditorAddedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+}
+
+type BranchEditorRemovedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ UserId string `json:"userId" msgpack:"userId"`
+}
diff --git a/qubership-apihub-service/websocket/WsClient.go b/qubership-apihub-service/websocket/WsClient.go
new file mode 100644
index 0000000..ddeb603
--- /dev/null
+++ b/qubership-apihub-service/websocket/WsClient.go
@@ -0,0 +1,80 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+import (
+ "crypto/rand"
+ "encoding/json"
+ "fmt"
+ "math/big"
+ "sync"
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/view"
+ ws "github.com/gorilla/websocket"
+)
+
+type WsClient struct {
+ Connection *ws.Conn
+ SessionId string
+ User view.User
+ ConnectedAt time.Time
+ UserColor string
+ mutex sync.RWMutex
+}
+
+func NewWsClient(connection *ws.Conn, sessionId string, user view.User) *WsClient {
+ client := &WsClient{
+ Connection: connection,
+ SessionId: sessionId,
+ ConnectedAt: time.Now(),
+ User: user,
+ UserColor: generateUserColor(),
+ mutex: sync.RWMutex{},
+ }
+ return client
+}
+
+func (c *WsClient) send(payload interface{}) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ err := c.Connection.WriteJSON(payload)
+ if err != nil {
+ c.Connection.Close()
+ return fmt.Errorf("failed to send message to sess %s: %s", c.SessionId, err.Error())
+ }
+ return nil
+}
+
+func (c *WsClient) MarshalJSON() ([]byte, error) {
+ return json.Marshal(&struct {
+ SessionId string `json:"wsId"`
+ User view.User `json:"user"`
+ ConnectedAt time.Time `json:"connectedAt"`
+ UserColor string `json:"userColor"`
+ }{
+ SessionId: c.SessionId,
+ User: c.User,
+ ConnectedAt: c.ConnectedAt,
+ UserColor: c.UserColor,
+ })
+}
+
+func generateUserColor() string {
+ r, _ := rand.Int(rand.Reader, big.NewInt(256))
+ g, _ := rand.Int(rand.Reader, big.NewInt(256))
+ b, _ := rand.Int(rand.Reader, big.NewInt(256))
+ return fmt.Sprintf("rgb(%d, %d, %d)", r, g, b)
+}
diff --git a/qubership-apihub-service/websocket/WsEditSession.go b/qubership-apihub-service/websocket/WsEditSession.go
new file mode 100644
index 0000000..4123afa
--- /dev/null
+++ b/qubership-apihub-service/websocket/WsEditSession.go
@@ -0,0 +1,283 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+import (
+ "encoding/json"
+ "sync"
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/utils"
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/view"
+ ws "github.com/gorilla/websocket"
+ log "github.com/sirupsen/logrus"
+)
+
+// WsEditSession General operations for websocket file/branch edit session
+type WsEditSession struct {
+ clients sync.Map
+ messageHandler WsMessageHandler
+ sessionClosedHandler SessionClosedHandler
+ EditSessionId string
+ registerClientsCh chan *RegMsg
+ OriginatorUserId string
+}
+
+type RegMsg struct {
+ client *WsClient
+ wg *sync.WaitGroup
+}
+
+func NewWsEditSession(editSessionId string, messageHandler WsMessageHandler, sessionClosedHandler SessionClosedHandler, originatorUserId string) *WsEditSession {
+ sess := &WsEditSession{
+ clients: sync.Map{},
+ messageHandler: messageHandler,
+ sessionClosedHandler: sessionClosedHandler,
+ EditSessionId: editSessionId,
+ OriginatorUserId: originatorUserId,
+ registerClientsCh: make(chan *RegMsg),
+ }
+
+ utils.SafeAsync(func() {
+ sess.runClientRegistration()
+ })
+ log.Debugf("Started WS edit session with id %s", editSessionId)
+ return sess
+}
+
+func (b *WsEditSession) ConnectClient(wsId string, conn *ws.Conn, user view.User, extWg *sync.WaitGroup) {
+ conn.SetReadDeadline(time.Now().Add(PingTime * 2))
+ conn.SetPongHandler(func(appData string) error {
+ conn.SetReadDeadline(time.Now().Add(PingTime * 2))
+ return nil
+ })
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+
+ b.registerClientsCh <- &RegMsg{NewWsClient(conn, wsId, user), &wg}
+
+ wg.Wait()
+ if extWg != nil {
+ extWg.Done()
+ }
+
+ utils.SafeAsync(func() {
+ b.handleIncomingMessages(conn, wsId, user)
+ })
+}
+
+func (b *WsEditSession) GetClient(wsId string) *WsClient {
+ if client, exists := b.clients.Load(wsId); exists {
+ return client.(*WsClient)
+ }
+ return nil
+}
+
+func (b *WsEditSession) runClientRegistration() {
+ for {
+ RegMsg, more := <-b.registerClientsCh
+ if RegMsg != nil {
+ client := RegMsg.client
+ b.clients.Store(client.SessionId, client)
+
+ //send "user:connected" notification to all other connected users
+ b.NotifyOthers(client.SessionId,
+ UserConnectedPatch{
+ Type: UserConnectedType,
+ SessionId: client.SessionId,
+ ConnectedAt: client.ConnectedAt,
+ User: client.User,
+ UserColor: client.UserColor,
+ })
+
+ //send "user:connected" notifications for each connected user to the current user
+ b.clients.Range(func(key, value interface{}) bool {
+ c := value.(*WsClient)
+ // use sync send method here
+ err := client.send(UserConnectedPatch{
+ Type: UserConnectedType,
+ SessionId: c.SessionId,
+ ConnectedAt: c.ConnectedAt,
+ User: c.User,
+ UserColor: c.UserColor,
+ })
+ if err != nil {
+ log.Errorf("Failed to send user:connected %v: %v", client.SessionId, err.Error())
+ return false
+ }
+ return true
+ })
+ RegMsg.wg.Done()
+ }
+ if !more {
+ return
+ }
+ }
+}
+
+func (b *WsEditSession) NotifyClient(wsId string, message interface{}) {
+ utils.SafeAsync(func() {
+ v, exists := b.clients.Load(wsId)
+ if exists {
+ client := v.(*WsClient)
+ err := client.send(message)
+ if err != nil {
+ log.Errorf("Failed to notify client %v: %v", client.SessionId, err.Error())
+ }
+ } else {
+ log.Debugf("Unable to send message '%s' since client %s not found", message, wsId)
+ }
+ })
+}
+
+func (b *WsEditSession) NotifyClientSync(wsId string, message interface{}) {
+ v, exists := b.clients.Load(wsId)
+ if exists {
+ client := v.(*WsClient)
+ err := client.send(message)
+ if err != nil {
+ log.Errorf("Failed to notify client sync %v: %v", client.SessionId, err.Error())
+ }
+ } else {
+ log.Debugf("Unable to send message '%s' since client %s not found", message, wsId)
+ }
+}
+
+func (b *WsEditSession) NotifyOthers(wsId string, message interface{}) {
+ utils.SafeAsync(func() {
+ b.clients.Range(func(key, value interface{}) bool {
+ c := value.(*WsClient)
+ if c.SessionId == wsId {
+ return true
+ }
+ err := c.send(message)
+ if err != nil {
+ log.Errorf("Failed to notify client %v: %v", c.SessionId, err.Error())
+ }
+ return true
+ })
+ })
+}
+
+func (b *WsEditSession) NotifyAll(message interface{}) {
+ utils.SafeAsync(func() {
+ b.clients.Range(func(key, value interface{}) bool {
+ c := value.(*WsClient)
+ err := c.send(message)
+ if err != nil {
+ log.Errorf("Failed to notify client %v: %v", c.SessionId, err.Error())
+ }
+ return true
+ })
+ })
+}
+
+func (b *WsEditSession) handleIncomingMessages(connection *ws.Conn, wsId string, user view.User) {
+ defer connection.Close()
+ for {
+ _, data, err := connection.ReadMessage()
+ if err != nil {
+ log.Debugf("Connection %v closed: %v", wsId, err.Error())
+ b.handleClientDisconnect(wsId)
+ break
+ }
+ if b.messageHandler != nil {
+ b.messageHandler.HandleMessage(data, wsId, b)
+ }
+ }
+}
+
+func (b *WsEditSession) handleClientDisconnect(wsId string) {
+ v, exists := b.clients.Load(wsId)
+ if !exists {
+ return
+ }
+ client := v.(*WsClient)
+
+ b.clients.Delete(wsId)
+
+ clientsCount := 0
+ b.clients.Range(func(key, value interface{}) bool {
+ clientsCount++
+ return true
+ })
+
+ if clientsCount > 0 {
+ b.NotifyAll(UserDisconnectedPatch{Type: UserDisconnectedType, SessionId: wsId, User: client.User})
+ if b.sessionClosedHandler != nil {
+ b.sessionClosedHandler.HandleUserDisconnected(b.EditSessionId, wsId)
+ }
+ } else {
+ close(b.registerClientsCh)
+ if b.sessionClosedHandler != nil {
+ b.sessionClosedHandler.HandleSessionClosed(b.EditSessionId)
+ }
+ log.Debugf("Closed WS edit session with id %s", b.EditSessionId)
+ }
+}
+
+func (b *WsEditSession) ForceDisconnectAll() {
+ utils.SafeAsync(func() {
+ b.clients.Range(func(key, value interface{}) bool {
+ c := value.(*WsClient)
+ c.Connection.Close()
+ return true
+ })
+ })
+}
+
+func (b *WsEditSession) ForceDisconnect(wsId string) {
+ v, exists := b.clients.Load(wsId)
+ if exists {
+ client := v.(*WsClient)
+ client.Connection.Close()
+ }
+}
+
+func (b *WsEditSession) MarshalJSON() ([]byte, error) {
+ var clients map[string]*WsClient
+ b.clients.Range(func(key, value interface{}) bool {
+ clients[key.(string)] = value.(*WsClient)
+ return true
+ })
+
+ return json.Marshal(&struct {
+ Clients map[string]*WsClient `json:"clients"`
+ EditSessionId string `json:"editSessionId"`
+ OriginatorUserId string `json:"originatorUserId"`
+ }{
+ Clients: clients,
+ EditSessionId: b.EditSessionId,
+ OriginatorUserId: b.OriginatorUserId,
+ })
+}
+
+const PingTime = time.Second * 5
+
+func (b *WsEditSession) SendPingToAllClients() {
+ b.clients.Range(func(key, value interface{}) bool {
+ client := value.(*WsClient)
+ utils.SafeAsync(func() {
+ if err := client.Connection.WriteControl(ws.PingMessage, []byte{}, time.Now().Add(PingTime)); err != nil {
+ log.Errorf("Can't send ping for %v", client.SessionId)
+ log.Debugf("Connection wsId=%v will be closed due to timeout: %v", client.SessionId, err.Error())
+ b.handleClientDisconnect(client.SessionId)
+ client.Connection.Close()
+ }
+ })
+ return true
+ })
+}
diff --git a/qubership-apihub-service/websocket/WsFileActions.go b/qubership-apihub-service/websocket/WsFileActions.go
new file mode 100644
index 0000000..79e346b
--- /dev/null
+++ b/qubership-apihub-service/websocket/WsFileActions.go
@@ -0,0 +1,96 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+import (
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/ot"
+)
+
+const (
+ UserCursorOutputType = "user:cursor"
+ OperationOutputType = "user:operation"
+ DocSnapshotOutputType = "document:snapshot"
+ UnexpectedMessageType = "message:unexpected"
+
+ UserCursorInputType = "cursor"
+ OperationInputMessageType = "operation"
+
+ DebugStateInputMessageType = "debug_state"
+)
+
+type TypedWsMessage struct {
+ Type string `json:"type" msgpack:"type"`
+}
+
+type UserCursorInput struct {
+ Type string `json:"type" msgpack:"type"`
+ Position int `json:"position" msgpack:"position"`
+ SelectionEnd int `json:"selectionEnd" msgpack:"selectionEnd"`
+}
+
+type UserCursorOutput struct {
+ Type string `json:"type" msgpack:"type"`
+ SessionId string `json:"sessionId" msgpack:"sessionId"`
+ Cursor CursorValue `json:"cursor" msgpack:"cursor"`
+}
+
+type CursorValue struct {
+ Position int `json:"position" msgpack:"position"`
+ SelectionEnd int `json:"selectionEnd" msgpack:"selectionEnd"`
+}
+
+type OperationInputMessage struct {
+ Type string `json:"type" msgpack:"type"`
+ Revision int `json:"revision" msgpack:"revision"`
+ Operation []interface{} `json:"operation" msgpack:"operation"`
+}
+
+type OperationOutputMessage struct {
+ Type string `json:"type" msgpack:"type"`
+ SessionId string `json:"sessionId" msgpack:"sessionId"`
+ Revision int `json:"revision" msgpack:"revision"`
+ Operation []interface{} `json:"operation" msgpack:"operation"`
+}
+
+type DocSnapshotOutputMessage struct {
+ Type string `json:"type" msgpack:"type"`
+ Revision int `json:"revision" msgpack:"revision"`
+ Document []string `json:"document" msgpack:"document"`
+}
+
+type UnexpectedMessage struct {
+ Type string `json:"type" msgpack:"type"`
+ Message interface{} `json:"message" msgpack:"message"`
+}
+
+type WsEvent struct {
+ EditSessionId string // file or branch id
+ WsId string
+ Data []byte
+}
+
+type OpsStatData struct {
+ LastSavedRev int
+ SaveTimestamp time.Time
+}
+
+type DebugSessionStateOutputMessage struct {
+ Session *WsEditSession
+ File *ot.ServerDoc
+ Cursors map[string]CursorValue
+ OpsStatData OpsStatData
+}
diff --git a/qubership-apihub-service/websocket/WsGeneralActions.go b/qubership-apihub-service/websocket/WsGeneralActions.go
new file mode 100644
index 0000000..70c7bd7
--- /dev/null
+++ b/qubership-apihub-service/websocket/WsGeneralActions.go
@@ -0,0 +1,40 @@
+// Copyright 2024-2025 NetCracker Technology Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package websocket
+
+import (
+ "time"
+
+ "github.com/Netcracker/qubership-apihub-backend/qubership-apihub-service/view"
+)
+
+const (
+ UserConnectedType = "user:connected"
+ UserDisconnectedType = "user:disconnected"
+)
+
+type UserConnectedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ SessionId string `json:"sessionId" msgpack:"sessionId"`
+ ConnectedAt time.Time `json:"connectedAt" msgpack:"connectedAt"`
+ User view.User `json:"user" msgpack:"user"`
+ UserColor string `json:"userColor" msgpack:"userColor"`
+}
+
+type UserDisconnectedPatch struct {
+ Type string `json:"type" msgpack:"type"`
+ SessionId string `json:"sessionId" msgpack:"sessionId"`
+ User view.User `json:"user" msgpack:"user"`
+}