diff --git a/.travis.yml b/.travis.yml index f28dc7230..a23fba418 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +sudo: true + language: go go: @@ -5,10 +7,32 @@ go: go_import_path: github.com/vmware/harbor -#service: -# - mysql +services: + - docker + - mysql -env: DB_HOST=127.0.0.1 DB_PORT=3306 DB_USR=root DB_PWD= +dist: trusty +addons: + apt: + packages: + - mysql-server-5.6 + - mysql-client-core-5.6 + - mysql-client-5.6 + +env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + DB_USR: root + DB_PWD: + DOCKER_COMPOSE_VERSION: 1.7.1 + HARBOR_ADMIN: admin + HARBOR_ADMIN_PASSWD: Harbor12345 + +before_install: + - ./tests/hostcfg.sh + - cd Deploy + - ./prepare + - cd .. install: - sudo apt-get update && sudo apt-get install -y libldap2-dev @@ -29,12 +53,29 @@ install: - go get -d github.com/go-sql-driver/mysql - go get github.com/golang/lint/golint - go get github.com/GeertJohan/fgt - + - sudo apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" docker-engine=1.11.1-0~trusty + - sudo rm /usr/local/bin/docker-compose + - curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose + - chmod +x docker-compose + - sudo mv docker-compose /usr/local/bin + - go get github.com/dghubble/sling + - go get github.com/stretchr/testify + before_script: # create tables and load data - mysql < ./Deploy/db/registry.sql -uroot --verbose script: - - go list ./... | grep -v /vendor/ | xargs -L1 fgt golint - - go list ./... | grep -v 'vendor' | xargs -L1 go vet - - go list ./... | grep -v 'vendor' | xargs -L1 go test -v + - go list ./... | grep -v 'tests' | grep -v /vendor/ | xargs -L1 fgt golint + - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go vet + - go list ./... | grep -v 'tests' | grep -v 'vendor' | xargs -L1 go test -v + + - docker-compose -f Deploy/docker-compose.yml up -d + + - docker ps + - go run tests/startuptest.go http://localhost/ + - go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD} + + + # test for API + - go test -v ./tests/apitests diff --git a/api/member.go b/api/member.go index b86e36f5d..685dc132e 100644 --- a/api/member.go +++ b/api/member.go @@ -33,7 +33,7 @@ type ProjectMemberAPI struct { } type memberReq struct { - Username string `json:"user_name"` + Username string `json:"username"` UserID int `json:"user_id"` Roles []int `json:"roles"` } @@ -104,7 +104,7 @@ func (pma *ProjectMemberAPI) Get() { log.Errorf("Error occurred in GetUser, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } - result["user_name"] = user.Username + result["username"] = user.Username result["user_id"] = pma.memberID result["roles"] = roleList pma.Data["json"] = result diff --git a/api/replication_policy.go b/api/replication_policy.go index d0e9946cb..26b5b0a61 100644 --- a/api/replication_policy.go +++ b/api/replication_policy.go @@ -14,8 +14,6 @@ import ( // RepPolicyAPI handles /api/replicationPolicies /api/replicationPolicies/:id/enablement type RepPolicyAPI struct { BaseAPI - policyID int64 - policy *models.RepPolicy } // Prepare validates whether the user has system admin role @@ -214,11 +212,11 @@ func (pa *RepPolicyAPI) UpdateEnablement() { return } - if pa.policy.Enabled == e.Enabled { + if policy.Enabled == e.Enabled { return } - if err := dao.UpdateRepPolicyEnablement(pa.policyID, e.Enabled); err != nil { + if err := dao.UpdateRepPolicyEnablement(id, e.Enabled); err != nil { log.Errorf("Failed to update policy enablement in DB, error: %v", err) pa.RenderError(http.StatusInternalServerError, "Internal Error") return @@ -226,18 +224,18 @@ func (pa *RepPolicyAPI) UpdateEnablement() { if e.Enabled == 1 { go func() { - if err := TriggerReplication(pa.policyID, "", nil, models.RepOpTransfer); err != nil { - log.Errorf("failed to trigger replication of %d: %v", pa.policyID, err) + if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { + log.Errorf("failed to trigger replication of %d: %v", id, err) } else { - log.Infof("replication of %d triggered", pa.policyID) + log.Infof("replication of %d triggered", id) } }() } else { go func() { - if err := postReplicationAction(pa.policyID, "stop"); err != nil { - log.Errorf("failed to stop replication of %d: %v", pa.policyID, err) + if err := postReplicationAction(id, "stop"); err != nil { + log.Errorf("failed to stop replication of %d: %v", id, err) } else { - log.Infof("try to stop replication of %d", pa.policyID) + log.Infof("try to stop replication of %d", id) } }() } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index e4c274984..4d94f4ce6 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -4,7 +4,7 @@ swagger: '2.0' info: title: Harbor API description: These APIs provide services for manipulating Harbor project. - version: "0.1.0" + version: "0.1.1" # the domain of the service host: localhost # array of all schemes that your API supports @@ -167,7 +167,7 @@ paths: - name: access_log in: body schema: - $ref: '#/definitions/AccessLog' + $ref: '#/definitions/AccessLogFilter' description: Search results of access logs. tags: - Products @@ -204,7 +204,7 @@ paths: schema: type: array items: - $ref: '#/definitions/Role' + $ref: '#/definitions/User' 400: description: Illegal format of provided ID value. 401: @@ -567,7 +567,7 @@ paths: schema: type: array items: - $ref: '#/definitions/Repository' + type: string 400: description: Invalid project ID. 403: @@ -719,12 +719,41 @@ definitions: description: Search results of the projects that matched the filter keywords. type: array items: - $ref: '#/definitions/Project' + $ref: '#/definitions/SearchProject' repositories: description: Search results of the repositories that matched the filter keywords. type: array items: - $ref: '#/definitions/Repository' + $ref: '#/definitions/SearchRepository' + SearchProject: + type: object + properties: + id: + type: integer + format: int64 + description: The ID of project + name: + type: string + description: The name of the project + public: + type: integer + format: int + description: The flag to indicate the publicity of the project (1 is public, 0 is non-public) + SearchRepository: + type: object + properties: + repository_name: + type: string + description: The name of the repository + project_name: + type: string + description: The name of the project that the repository belongs to + project_id: + type: integer + description: The ID of the project that the repository belongs to + project_public: + type: integer + description: The flag to indicate the publicity of the project that the repository belongs to (1 is public, 0 is not) Project: type: object properties: @@ -736,30 +765,39 @@ definitions: type: integer format: int32 description: The owner ID of the project always means the creator of the project. - project_name: + name: type: string description: The name of the project. creation_time: type: string description: The creation time of the project. + update_time: + type: string + description: The update time of the project. deleted: type: integer format: int32 - description: A deletion mark of the project. + description: A deletion mark of the project (1 means it's deleted, 0 is not) user_id: type: integer format: int32 description: A relation field to the user table. owner_name: type: string - description: The owner name of tthe project always means the creator of the project. + description: The owner name of the project. public: type: boolean format: boolean description: The public status of the project. togglable: type: boolean - description: Correspond to the UI about showing the public status of the project. + description: Correspond to the UI about whether the project's publicity is updatable (for UI) + current_user_role_id: + type: integer + description: The role ID of the current user who triggered the API (for UI) + repo_count: + type: integer + description: The number of the repositories under this project. Repository: type: object properties: @@ -794,6 +832,7 @@ definitions: user_id: type: integer format: int32 + description: The ID of the user. username: type: string email: @@ -816,7 +855,7 @@ definitions: new_password: type: string description: New password for marking as to be updated. - AccessLog: + AccessLogFilter: type: object properties: username: @@ -825,14 +864,32 @@ definitions: keywords: type: string description: Operation name specified when project created. - beginTimestamp: + begin_timestamp: type: integer - format: int32 + format: int64 description: Begin timestamp for querying access logs. - endTimestamp: + end_timestamp: type: integer - format: int32 + format: int64 description: End timestamp for querying accessl logs. + AccessLog: + type: object + properties: + log_id: + type: integer + description: The ID of the log entry. + repo_name: + type: string + description: Name of the repository in this log entry. + repo_tag: + type: string + description: Tag of the repository in this log entry. + operation: + type: string + description: The operation against the repository in this log entry. + op_time: + type: time + description: The time when this operation is triggered. Role: type: object properties: @@ -855,7 +912,7 @@ definitions: type: integer format: int32 description: Role ID for updating project role member. - user_name: + username: type: string description: Username relevant to a project role member. TopRepo: diff --git a/models/accesslog.go b/models/accesslog.go index b5d919943..336e7bd08 100644 --- a/models/accesslog.go +++ b/models/accesslog.go @@ -21,19 +21,18 @@ import ( // AccessLog holds information about logs which are used to record the actions that user take to the resourses. type AccessLog struct { - LogID int `orm:"pk;column(log_id)" json:"LogId"` - UserID int `orm:"column(user_id)" json:"UserId"` - ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` - RepoName string `orm:"column(repo_name)"` - RepoTag string `orm:"column(repo_tag)"` - GUID string `orm:"column(GUID)" json:"Guid"` - Operation string `orm:"column(operation)"` - OpTime time.Time `orm:"column(op_time)"` - Username string - Keywords string - + LogID int `orm:"column(log_id)" json:"log_id"` + UserID int `orm:"column(user_id)" json:"user_id"` + ProjectID int64 `orm:"column(project_id)" json:"project_id"` + RepoName string `orm:"column(repo_name)" json:"repo_name"` + RepoTag string `orm:"column(repo_tag)" json:"repo_tag"` + GUID string `orm:"column(GUID)" json:"guid"` + Operation string `orm:"column(operation)" json:"operation"` + OpTime time.Time `orm:"column(op_time)" json:"op_time"` + Username string `json:"username"` + Keywords string `json:"keywords"` BeginTime time.Time - BeginTimestamp int64 + BeginTimestamp int64 `json:"begin_timestamp"` EndTime time.Time - EndTimestamp int64 + EndTimestamp int64 `json:"end_timestamp"` } diff --git a/models/project.go b/models/project.go index f2a374e4c..7f5d54c25 100644 --- a/models/project.go +++ b/models/project.go @@ -21,19 +21,19 @@ import ( // Project holds the details of a project. type Project struct { - ProjectID int64 `orm:"pk;column(project_id)" json:"ProjectId"` - OwnerID int `orm:"column(owner_id)" json:"OwnerId"` - Name string `orm:"column(name)"` - CreationTime time.Time `orm:"column(creation_time)"` - CreationTimeStr string - Deleted int `orm:"column(deleted)"` - UserID int `json:"UserId"` - OwnerName string - Public int `orm:"column(public)"` + ProjectID int64 `orm:"column(project_id)" json:"project_id"` + OwnerID int `orm:"column(owner_id)" json:"owner_id"` + Name string `orm:"column(name)" json:"name"` + CreationTime time.Time `orm:"column(creation_time)" json:"creation_time"` + CreationTimeStr string `json:"creation_time_str"` + Deleted int `orm:"column(deleted)" json:"deleted"` + //UserID int `json:"UserId"` + OwnerName string `json:"owner_name"` + Public int `orm:"column(public)" json:"public"` //This field does not have correspondent column in DB, this is just for UI to disable button Togglable bool UpdateTime time.Time `orm:"update_time" json:"update_time"` - Role int `json:"role_id"` + Role int `json:"current_user_role_id"` RepoCount int `json:"repo_count"` } diff --git a/models/user.go b/models/user.go index 3f0612c2e..241a25549 100644 --- a/models/user.go +++ b/models/user.go @@ -21,20 +21,19 @@ import ( // User holds the details of a user. type User struct { - UserID int `orm:"pk;column(user_id)" json:"UserId"` - Username string `orm:"column(username)" json:"username"` - Email string `orm:"column(email)" json:"email"` - Password string `orm:"column(password)" json:"password"` - Realname string `orm:"column(realname)" json:"realname"` - Comment string `orm:"column(comment)" json:"comment"` - Deleted int `orm:"column(deleted)"` - Rolename string - RoleID int `json:"RoleId"` - RoleList []*Role `orm:"rel(m2m)"` - HasAdminRole int `orm:"column(sysadmin_flag)"` - ResetUUID string `orm:"column(reset_uuid)" json:"ResetUuid"` - Salt string `orm:"column(salt)"` - + UserID int `orm:"column(user_id)" json:"user_id"` + Username string `orm:"column(username)" json:"username"` + Email string `orm:"column(email)" json:"email"` + Password string `orm:"column(password)" json:"password"` + Realname string `orm:"column(realname)" json:"realname"` + Comment string `orm:"column(comment)" json:"comment"` + Deleted int `orm:"column(deleted)" json:"deleted"` + Rolename string `json:"role_name"` + RoleID int `json:"role_id"` + // RoleList []Role `json:"role_list"` + HasAdminRole int `orm:"column(sysadmin_flag)" json:"has_admin_role"` + ResetUUID string `orm:"column(reset_uuid)" json:"reset_uuid"` + Salt string `orm:"column(salt)"` CreationTime time.Time `orm:"creation_time" json:"creation_time"` UpdateTime time.Time `orm:"update_time" json:"update_time"` } diff --git a/static/resources/css/base.css b/static/resources/css/base.css index 0f8b9bd14..685da65ea 100644 --- a/static/resources/css/base.css +++ b/static/resources/css/base.css @@ -13,7 +13,6 @@ limitations under the License. */ .footer { - margin-top: 60px; width: 100%; /* Set the fixed height of the footer here */ height: 60px; diff --git a/static/resources/js/change-password.js b/static/resources/js/change-password.js index 4c8c2efdc..0a536b7b4 100644 --- a/static/resources/js/change-password.js +++ b/static/resources/js/change-password.js @@ -24,7 +24,7 @@ jQuery(function(){ error: function(jqXhr){ if(jqXhr && jqXhr.status == 401){ document.location = "/signIn"; - } + } } }).exec(); @@ -36,12 +36,12 @@ jQuery(function(){ function bindEnterKey(){ $(document).on("keydown", function(e){ if(e.keyCode == 13){ - e.preventDefault(); - if($("#txtCommonSearch").is(":focus")){ - document.location = "/search?q=" + $("#txtCommonSearch").val(); - }else{ - $("#btnSubmit").trigger("click"); - } + e.preventDefault(); + if($("#txtCommonSearch").is(":focus")){ + document.location = "/search?q=" + $("#txtCommonSearch").val(); + }else{ + $("#btnSubmit").trigger("click"); + } } }); } @@ -61,35 +61,35 @@ jQuery(function(){ type: "put", data: {"old_password": oldPassword, "new_password" : password}, beforeSend: function(e){ - unbindEnterKey(); - $("h1").append(spinner.el); - $("#btnSubmit").prop("disabled", true); + unbindEnterKey(); + $("h1").append(spinner.el); + $("#btnSubmit").prop("disabled", true); }, complete: function(xhr, status){ spinner.stop(); $("#btnSubmit").prop("disabled", false); if(xhr && xhr.status == 200){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_change_password"), - "content": i18n.getMessage("change_password_successfully"), - "callback": function(){ - window.close(); - } - }); + .dialogModal({ + "title": i18n.getMessage("title_change_password"), + "content": i18n.getMessage("change_password_successfully"), + "callback": function(){ + window.close(); + } + }); } }, error: function(jqXhr, status, error){ if(jqXhr && jqXhr.responseText.length){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_change_password"), - "content": i18n.getMessage(jqXhr.responseText), - "callback": function(){ - bindEnterKey(); - return; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_change_password"), + "content": i18n.getMessage(jqXhr.responseText), + "callback": function(){ + bindEnterKey(); + return; + } + }); } } }).exec(); diff --git a/static/resources/js/common.js b/static/resources/js/common.js index 78cdfc638..f434d0960 100644 --- a/static/resources/js/common.js +++ b/static/resources/js/common.js @@ -12,8 +12,9 @@ See the License for the specific language governing permissions and limitations under the License. */ + var AjaxUtil = function(params){ - + this.url = params.url; this.data = params.data; this.dataRaw = params.dataRaw; @@ -31,39 +32,39 @@ AjaxUtil.prototype.exec = function(){ var self = this; return $.ajax({ - url: self.url, - contentType: (self.dataRaw ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8"), - data: JSON.stringify(self.data) || self.dataRaw, - type: self.type, - dataType: "json", - success: function(data, status, xhr){ - if(self.success != null){ - self.success(data, status, xhr); - } - }, - complete: function(jqXhr, status) { - if(self.complete != null){ - self.complete(jqXhr, status); - } - }, - error: function(jqXhr){ - if(self.error != null){ - self.error(jqXhr); - }else{ - var errorMessage = self.errors[jqXhr.status] || jqXhr.responseText; - if(jqXhr.status == 401){ - var lastUri = location.pathname + location.search; - if(lastUri != ""){ - document.location = "/signIn?uri=" + encodeURIComponent(lastUri); - }else{ - document.location = "/signIn"; + url: self.url, + contentType: (self.dataRaw ? "application/x-www-form-urlencoded; charset=UTF-8" : "application/json; charset=utf-8"), + data: JSON.stringify(self.data) || self.dataRaw, + type: self.type, + dataType: "json", + success: function(data, status, xhr){ + if(self.success != null){ + self.success(data, status, xhr); + } + }, + complete: function(jqXhr, status) { + if(self.complete != null){ + self.complete(jqXhr, status); + } + }, + error: function(jqXhr){ + if(self.error != null){ + self.error(jqXhr); + }else{ + var errorMessage = self.errors[jqXhr.status] || jqXhr.responseText; + if(jqXhr.status == 401){ + var lastUri = location.pathname + location.search; + if(lastUri != ""){ + document.location = "/signIn?uri=" + encodeURIComponent(lastUri); + }else{ + document.location = "/signIn"; + } + }else if($.trim(errorMessage).length > 0){ + $("#dlgModal").dialogModal({"title": i18n.getMessage("operation_failed"), "content": errorMessage}); } - }else if($.trim(errorMessage).length > 0){ - $("#dlgModal").dialogModal({"title": i18n.getMessage("operation_failed"), "content": errorMessage}); } } - } - }); + }); }; var SUPPORT_LANGUAGES = { @@ -134,7 +135,7 @@ jQuery(function(){ var self = this; $("#dlgLabel", self).text(settings.title); - + if(options.text){ $("#dlgBody", self).html(settings.content); }else if(typeof settings.content == "object"){ @@ -142,9 +143,9 @@ jQuery(function(){ var lines = ['
']; for(var item in settings.content){ lines.push('
'+ - '' + - '

' + settings.content[item] + '

' + - '
'); + '' + + '

' + settings.content[item] + '

' + + ''); } lines.push('
'); $("#dlgBody", self).html(lines.join("")); @@ -154,8 +155,13 @@ jQuery(function(){ } if(settings.callback != null){ - $("#dlgConfirm").on("click", function(){ - settings.callback(); + var hasEntered = false; + $("#dlgConfirm").on("click", function(e){ + if(!hasEntered) { + hasEntered = true; + settings.callback(); + + } }); } $(self).modal('show'); diff --git a/static/resources/js/forgot-password.js b/static/resources/js/forgot-password.js index b7c4be9e6..d6fdea319 100644 --- a/static/resources/js/forgot-password.js +++ b/static/resources/js/forgot-password.js @@ -13,26 +13,26 @@ limitations under the License. */ jQuery(function(){ - + $("#divErrMsg").css({"display": "none"}); validateOptions.Items = ["#EmailF"]; function bindEnterKey(){ $(document).on("keydown", function(e){ if(e.keyCode == 13){ - e.preventDefault(); - if($("#txtCommonSearch").is(":focus")){ - document.location = "/search?q=" + $("#txtCommonSearch").val(); - }else{ - $("#btnSubmit").trigger("click"); - } + e.preventDefault(); + if($("#txtCommonSearch").is(":focus")){ + document.location = "/search?q=" + $("#txtCommonSearch").val(); + }else{ + $("#btnSubmit").trigger("click"); + } } }); } function unbindEnterKey(){ $(document).off("keydown"); } - bindEnterKey(); + bindEnterKey(); var spinner = new Spinner({scale:1}).spin(); $("#btnSubmit").on("click", function(){ @@ -44,20 +44,20 @@ jQuery(function(){ "type": "get", "data": {"username": username, "email": email}, "beforeSend": function(e){ - unbindEnterKey(); - $("h1").append(spinner.el); - $("#btnSubmit").prop("disabled", true); + unbindEnterKey(); + $("h1").append(spinner.el); + $("#btnSubmit").prop("disabled", true); }, "success": function(data, status, xhr){ if(xhr && xhr.status == 200){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_forgot_password"), - "content": i18n.getMessage("email_has_been_sent"), - "callback": function(){ - document.location="/"; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_forgot_password"), + "content": i18n.getMessage("email_has_been_sent"), + "callback": function(){ + document.location="/"; + } + }); } }, @@ -68,14 +68,14 @@ jQuery(function(){ "error": function(jqXhr, status, error){ if(jqXhr){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_forgot_password"), - "content": i18n.getMessage(jqXhr.responseText), - "callback": function(){ - bindEnterKey(); - return; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_forgot_password"), + "content": i18n.getMessage(jqXhr.responseText), + "callback": function(){ + bindEnterKey(); + return; + } + }); } } }); diff --git a/static/resources/js/header-login.js b/static/resources/js/header-login.js index 8e1a6e35e..1bd5cda0d 100644 --- a/static/resources/js/header-login.js +++ b/static/resources/js/header-login.js @@ -13,20 +13,20 @@ limitations under the License. */ jQuery(function(){ - $("#btnSignUp").css({"visibility": "visible"}); + $("#btnSignUp").css({"visibility": "visible"}); $(document).on("keydown", function(e){ if(e.keyCode == 13){ e.preventDefault(); if($("#txtCommonSearch").is(":focus")){ - document.location = "/search?q=" + $("#txtCommonSearch").val(); + document.location = "/search?q=" + $("#txtCommonSearch").val(); } } }); $("#btnSignIn").on("click", function(){ document.location = "/signIn"; }); - $("#btnSignUp").on("click", function(){ + $("#btnSignUp").on("click", function(){ document.location = "/register"; }); }); \ No newline at end of file diff --git a/static/resources/js/item-detail.js b/static/resources/js/item-detail.js index c63b1a69b..bfab81536 100644 --- a/static/resources/js/item-detail.js +++ b/static/resources/js/item-detail.js @@ -23,461 +23,451 @@ jQuery(function(){ if(jqXhr.status == 403){ return false; } - } + } } }).exec() - ).then(function(){ - noNeedToLoginCallback(); - needToLoginCallback(); - }).fail(function(){ - noNeedToLoginCallback(); - }); - - function noNeedToLoginCallback(){ - - $("#tabItemDetail a:first").tab("show"); - $("#btnFilterOption button:first").addClass("active"); - $("#divErrMsg").hide(); - - if($("#public").val() == 1){ - $("#tabItemDetail li:eq(1)").hide(); - $("#tabItemDetail li:eq(2)").hide(); - } - - listRepo($("#repoName").val()); - - function listRepo(repoName){ + ).then(function(){ + noNeedToLoginCallback(); + needToLoginCallback(); + }).fail(function(){ + noNeedToLoginCallback(); + }); + function noNeedToLoginCallback(){ + + $("#tabItemDetail a:first").tab("show"); + $("#btnFilterOption button:first").addClass("active"); $("#divErrMsg").hide(); + + if($("#public").val() == 1){ + $("#tabItemDetail li:eq(1)").hide(); + $("#tabItemDetail li:eq(2)").hide(); + } + + listRepo($("#repoName").val()); + + function listRepo(repoName){ + + $("#divErrMsg").hide(); - new AjaxUtil({ - url: "/api/repositories?project_id=" + $("#projectId").val() + "&q=" + repoName, - type: "get", - success: function(data, status, xhr){ - if(xhr && xhr.status == 200){ - $("#accordionRepo").children().remove(); - if(data == null){ - $("#divErrMsg").show(); - $("#divErrMsg center").html(i18n.getMessage("no_repo_exists")); - return; - } - $.each(data, function(i, e){ - var targetId = e.replace(/\//g, "------").replace(/\./g, "---"); - var row = '
' + + new AjaxUtil({ + url: "/api/repositories?project_id=" + $("#projectId").val() + "&q=" + repoName, + type: "get", + success: function(data, status, xhr){ + if(xhr && xhr.status == 200){ + $("#accordionRepo").children().remove(); + if(data == null){ + $("#divErrMsg").show(); + $("#divErrMsg center").html(i18n.getMessage("no_repo_exists")); + return; + } + $.each(data, function(i, e){ + var targetId = e.replace(/\//g, "------").replace(/\./g, "---"); + var row = '
' + '' + - '
' + - '
' + - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + i18n.getMessage("tag")+ ' ' + i18n.getMessage("pull_command") + '
' - '
' + - '
' + - '
' + - '
'; - $("#accordionRepo").append(row); - }); - if(repoName != ""){ - $("#txtRepoName").val(repoName); - $("#accordionRepo #heading0 a").trigger("click"); + '

' + + '' + + ' ' + e + ' ' + + '' + + '

' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + i18n.getMessage("tag")+ ' ' + i18n.getMessage("pull_command") + '
' + '
' + + '
' + + '
' + + ''; + $("#accordionRepo").append(row); + }); + if(repoName != ""){ + $("#txtRepoName").val(repoName); + $("#accordionRepo #heading0 a").trigger("click"); + } } } - } - }).exec(); - } - $("#btnSearchRepo").on("click", function(){ - listRepo($.trim($("#txtRepoName").val())); - }); - - $('#accordionRepo').on('show.bs.collapse', function (e) { - $('#accordionRepo .in').collapse('hide'); - var targetId = $(e.target).attr("targetId"); - var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, '.'); - new AjaxUtil({ - url: "/api/repositories/tags?repo_name=" + repoName, - type: "get", - success: function(data, status, xhr){ - $('#' + targetId +' table tbody tr').remove(); - var row = []; - for(var i in data){ - var tagName = data[i]; - row.push('' + tagName + ''); - } - $('#' + targetId +' table tbody').append(row.join("")); - $('#' + targetId +' table tbody tr a').on("click", function(e){ - var imageId = $(this).attr("imageId"); - var repoName = $(this).attr("repoName"); - new AjaxUtil({ - url: "/api/repositories/manifests?repo_name=" + repoName + "&tag=" + imageId, - type: "get", - success: function(data, status, xhr){ - if(data){ - for(var i in data){ - if(data[i] == ""){ - data[i] = "N/A"; - } - } - data.Created = moment(new Date(data.Created)).format("YYYY-MM-DD HH:mm:ss"); - - $("#dlgModal").dialogModal({"title": i18n.getMessage("image_details"), "content": data}); - } - } - }).exec(); - }); - } - }).exec(); - }); - } - - function needToLoginCallback(){ - - var hasAuthorization = false; - - $.when( - new AjaxUtil({ - url: "/api/projects/" + $("#projectId").val() + "/members/current", - type: "get", - success: function(data, status, xhr){ - if(xhr && xhr.status == 200 && data.roles != null && data.roles.length > 0){ - hasAuthorization = true; - } - } - }).exec()) - .done(function(){ - - if(!hasAuthorization) return false; - - $("#tabItemDetail a:eq(1)").css({"visibility": "visible"}); - $("#tabItemDetail a:eq(2)").css({"visibility": "visible"}); - - $(".glyphicon .glyphicon-pencil", "#tblUser").on("click", function(e){ - $("#txtUserName").hide(); - $("#lblUserName").show(); - $("#dlgUserTitle").text(i18n.getMessage("edit_members")); - }); - - $("#btnAddUser").on("click", function(){ - $("#operationType").val("add"); - $("#spnSearch").show(); - $("#txtUserName").prop("disabled", false) - $("#txtUserName").val(""); - $("#lstRole input[name=chooseRole]:radio").prop("checked", false); - $("#dlgUserTitle").text(i18n.getMessage("add_members")); - }); - - $("#btnSave").on("click", function(){ - - var username = $("#txtUserName").val(); - if($.trim(username).length == 0){ - $("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_input_username")}); - return; - } - var projectId = $("#projectId").val(); - var operationType = $("#operationType").val(); - var userId = $("#editUserId").val(); - - var checkedRole = $("#lstRole input[name='chooseRole']:checked") - if(checkedRole.length == 0){ - $("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_assign_a_role_to_user")}); - return; - } - - var checkedRoleItemList = []; - $.each(checkedRole, function(i, e){ - checkedRoleItemList.push(new Number($(this).val())); - }); - - var ajaxOpts = {}; - if(operationType == "add"){ - ajaxOpts.url = "/api/projects/" + projectId + "/members/"; - ajaxOpts.type = "post"; - ajaxOpts.data = {"roles" : checkedRoleItemList, "user_name": username}; - }else if(operationType == "edit"){ - ajaxOpts.url = "/api/projects/" + projectId + "/members/" + userId; - ajaxOpts.type = "put"; - ajaxOpts.data = {"roles" : checkedRoleItemList}; - } - - new AjaxUtil({ - url: ajaxOpts.url, - data: ajaxOpts.data, - type: ajaxOpts.type, - complete: function(jqXhr, status){ - if(jqXhr && jqXhr.status == 200){ - $("#btnCancel").trigger("click"); - listUser(null); - } - }, - errors: { - 404: i18n.getMessage("user_id_does_not_exist"), - 409: i18n.getMessage("user_id_exists"), - 403: i18n.getMessage("insufficient_privileges") - } - }).exec(); - }); - - var name_mapping = { - "projectAdmin": "Project Admin", - "developer": "Developer", - "guest": "Guest" - } - - function listUserByProjectCallback(userList){ - var loginedUserId = $("#userId").val(); - var loginedUserRoleId = $("#roleId").val(); - var ownerId = $("#ownerId").val(); - - $("#tblUser tbody tr").remove(); - for(var i = 0; i < userList.length; ){ - - var userId = userList[i].UserId; - var roleId = userList[i].RoleId; - var username = userList[i].username; - var roleNameList = []; - - for(var j = i; j < userList.length; i++, j++){ - if(userList[j].UserId == userId){ - roleNameList.push(name_mapping[userList[j].Rolename]); - }else{ - break; - } - } - - var row = '' + - '' + username + '' + - '' + roleNameList.join(",") + '' + - ''; - var isShowOperations = true; - if(loginedUserRoleId >= 3 /*role: developer guest*/){ - isShowOperations = false; - }else if(ownerId == userId){ - isShowOperations = false; - }else if (loginedUserId == userId){ - isShowOperations = false; - } - if(isShowOperations){ - row += ' ' + - ''; - } - - row += ''; - $("#tblUser tbody").append(row); - - } - } - - function searchAccessLogCallback(LogList){ - $("#tabOperationLog tbody tr").remove(); - $.each(LogList || [], function(i, e){ - $("#tabOperationLog tbody").append( - '' + - '' + e.Username + '' + - '' + e.RepoName + '' + - '' + e.RepoTag + '' + - '' + e.Operation + '' + - '' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '' + - ''); - }); - } - - function getUserRoleCallback(userId){ - new AjaxUtil({ - url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, - type: "get", - success: function(data, status, xhr){ - var user = data; - $("#operationType").val("edit"); - $("#editUserId").val(user.user_id); - $("#spnSearch").hide(); - $("#txtUserName").val(user.user_name); - $("#txtUserName").prop("disabled", true); - $("#btnSave").removeClass("disabled"); - $("#dlgUserTitle").text(i18n.getMessage("edit_members")); - $("#lstRole input[name=chooseRole]:radio").not('[value=' + user.role_id + ']').prop("checked", false) - $.each(user.roles, function(i, e){ - $("#lstRole input[name=chooseRole]:radio").filter('[value=' + e.role_id + ']').prop("checked", "checked"); - }); - } }).exec(); } - function listUser(username){ - $.when( - new AjaxUtil({ - url: "/api/projects/" + $("#projectId").val() + "/members?username=" + (username == null ? "" : username), - type: "get", - errors: { - 403: "" - }, - success: function(data, status, xhr){ - return data || []; - } - }).exec() - ).done(function(userList){ - listUserByProjectCallback(userList || []); - $("#tblUser .glyphicon-pencil").on("click", function(e){ - var userId = $(this).attr("userid") - getUserRoleCallback(userId); - }); - $("#tblUser .glyphicon-trash").on("click", function(){ - var userId = $(this).attr("userid"); - new AjaxUtil({ - url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, - type: "delete", - complete: function(jqXhr, status){ - if(jqXhr && jqXhr.status == 200){ - listUser(null); - } - } - }).exec(); - }); - }); - } - listUser(null); - listOperationLogs(); - - function listOperationLogs(){ - var projectId = $("#projectId").val(); - - $.when( - new AjaxUtil({ - url : "/api/projects/" + projectId + "/logs/filter", - data: {}, - type: "post", - success: function(data){ - return data || []; - } - }).exec() - ).done(function(operationLogs){ - searchAccessLogCallback(operationLogs); - }); - } - - $("#btnSearchUser").on("click", function(){ - var username = $("#txtSearchUser").val(); - if($.trim(username).length == 0){ - username = null; - } - listUser(username); + $("#btnSearchRepo").on("click", function(){ + listRepo($.trim($("#txtRepoName").val())); }); - function toUTCSeconds(date, hour, min, sec) { - var t = new Date(date); - t.setHours(hour); - t.setMinutes(min); - t.setSeconds(sec); - var utcTime = new Date(t.getUTCFullYear(), + $('#accordionRepo').on('show.bs.collapse', function (e) { + $('#accordionRepo .in').collapse('hide'); + var targetId = $(e.target).attr("targetId"); + var repoName = targetId.replace(/[-]{6}/g, "/").replace(/[-]{3}/g, "."); + new AjaxUtil({ + url: "/api/repositories/tags?repo_name=" + repoName, + type: "get", + success: function(data, status, xhr){ + $('#' + targetId +' table tbody tr').remove(); + var row = []; + for(var i in data){ + var tagName = data[i] + row.push('' + tagName + ''); + } + $('#' + targetId +' table tbody').append(row.join("")); + $('#' + targetId +' table tbody tr a').on("click", function(e){ + var imageId = $(this).attr("imageId"); + var repoName = $(this).attr("repoName"); + new AjaxUtil({ + url: "/api/repositories/manifests?repo_name=" + repoName + "&tag=" + imageId, + type: "get", + success: function(data, status, xhr){ + if(data){ + for(var i in data){ + if(data[i] == ""){ + data[i] = "N/A"; + } + } + data.Created = moment(new Date(data.Created)).format("YYYY-MM-DD HH:mm:ss"); + $("#dlgModal").dialogModal({"title": i18n.getMessage("image_details"), "content": data}); + } + } + }).exec(); + }); + } + }).exec(); + }); + } + + function needToLoginCallback(){ + + var hasAuthorization = false; + + $.when( + new AjaxUtil({ + url: "/api/projects/" + $("#projectId").val() + "/members/current", + type: "get", + success: function(data, status, xhr){ + if(xhr && xhr.status == 200 && data.roles != null && data.roles.length > 0){ + hasAuthorization = true; + } + } + }).exec()) + .done(function(){ + + if(!hasAuthorization) return false; + + $("#tabItemDetail a:eq(1)").css({"visibility": "visible"}); + $("#tabItemDetail a:eq(2)").css({"visibility": "visible"}); + + $(".glyphicon .glyphicon-pencil", "#tblUser").on("click", function(e){ + $("#txtUserName").hide(); + $("#lblUserName").show(); + $("#dlgUserTitle").text(i18n.getMessage("edit_members")); + }); + + $("#btnAddUser").on("click", function(){ + $("#operationType").val("add"); + $("#spnSearch").show(); + $("#txtUserName").prop("disabled", false) + $("#txtUserName").val(""); + $("#lstRole input[name=chooseRole]:radio").prop("checked", false); + $("#dlgUserTitle").text(i18n.getMessage("add_members")); + }); + + $("#btnSave").on("click", function(){ + + var username = $("#txtUserName").val(); + if($.trim(username).length == 0){ + $("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_input_username")}); + return; + } + var projectId = $("#projectId").val(); + var operationType = $("#operationType").val(); + var userId = $("#editUserId").val(); + + var checkedRole = $("#lstRole input[name='chooseRole']:checked") + if(checkedRole.length == 0){ + $("#dlgModal").dialogModal({"title": i18n.getMessage("add_member_failed"), "content": i18n.getMessage("please_assign_a_role_to_user")}); + return; + } + + var checkedRoleItemList = []; + $.each(checkedRole, function(i, e){ + checkedRoleItemList.push(new Number($(this).val())); + }); + + var ajaxOpts = {}; + if(operationType == "add"){ + ajaxOpts.url = "/api/projects/" + projectId + "/members/"; + ajaxOpts.type = "post"; + ajaxOpts.data = {"roles" : checkedRoleItemList, "username": username}; + }else if(operationType == "edit"){ + ajaxOpts.url = "/api/projects/" + projectId + "/members/" + userId; + ajaxOpts.type = "put"; + ajaxOpts.data = {"roles" : checkedRoleItemList}; + } + + new AjaxUtil({ + url: ajaxOpts.url, + data: ajaxOpts.data, + type: ajaxOpts.type, + complete: function(jqXhr, status){ + if(jqXhr && jqXhr.status == 200){ + $("#btnCancel").trigger("click"); + listUser(null); + } + }, + errors: { + 404: i18n.getMessage("user_id_does_not_exist"), + 409: i18n.getMessage("user_id_exists"), + 403: i18n.getMessage("insufficient_privileges") + } + }).exec(); + }); + + var name_mapping = { + "projectAdmin": "Project Admin", + "developer": "Developer", + "guest": "Guest" + } + + function listUserByProjectCallback(userList){ + var loginedUserId = $("#userId").val(); + var loginedUserRoleId = $("#roleId").val(); + var ownerId = $("#ownerId").val(); + + $("#tblUser tbody tr").remove(); + for(var i = 0; i < userList.length; i++){ + + var userId = userList[i].user_id; + var roleId = userList[i].role_id; + var username = userList[i].username; + + var row = '' + + '' + username + '' + + '' + name_mapping[userList[i].role_name] + '' + + ''; + var isShowOperations = true; + if(loginedUserRoleId >= 3 /*role: developer guest*/){ + isShowOperations = false; + }else if(ownerId == userId){ + isShowOperations = false; + }else if (loginedUserId == userId){ + isShowOperations = false; + } + if(isShowOperations){ + row += ' ' + + ''; + } + + row += ''; + $("#tblUser tbody").append(row); + + } + } + + function searchAccessLogCallback(LogList){ + $("#tabOperationLog tbody tr").remove(); + $.each(LogList || [], function(i, e){ + $("#tabOperationLog tbody").append( + '' + + '' + e.username + '' + + '' + e.repo_name + '' + + '' + e.repo_tag + '' + + '' + e.operation + '' + + '' + moment(new Date(e.op_time)).format("YYYY-MM-DD HH:mm:ss") + '' + + ''); + }); + } + + function getUserRoleCallback(userId){ + new AjaxUtil({ + url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, + type: "get", + success: function(data, status, xhr){ + var user = data; + $("#operationType").val("edit"); + $("#editUserId").val(user.user_id); + $("#spnSearch").hide(); + $("#txtUserName").val(user.username); + $("#txtUserName").prop("disabled", true); + $("#btnSave").removeClass("disabled"); + $("#dlgUserTitle").text(i18n.getMessage("edit_members")); + $("#lstRole input[name=chooseRole]:radio").not('[value=' + user.role_id + ']').prop("checked", false) + $.each(user.roles, function(i, e){ + $("#lstRole input[name=chooseRole]:radio").filter('[value=' + e.role_id + ']').prop("checked", "checked"); + }); + } + }).exec(); + } + function listUser(username){ + $.when( + new AjaxUtil({ + url: "/api/projects/" + $("#projectId").val() + "/members?username=" + (username == null ? "" : username), + type: "get", + errors: { + 403: "" + }, + success: function(data, status, xhr){ + return data || []; + } + }).exec() + ).done(function(userList){ + listUserByProjectCallback(userList || []); + $("#tblUser .glyphicon-pencil").on("click", function(e){ + var userId = $(this).attr("userid") + getUserRoleCallback(userId); + }); + $("#tblUser .glyphicon-trash").on("click", function(){ + var userId = $(this).attr("userid"); + new AjaxUtil({ + url: "/api/projects/" + $("#projectId").val() + "/members/" + userId, + type: "delete", + complete: function(jqXhr, status){ + if(jqXhr && jqXhr.status == 200){ + listUser(null); + } + } + }).exec(); + }); + }); + } + listUser(null); + listOperationLogs(); + + function listOperationLogs(){ + var projectId = $("#projectId").val(); + + $.when( + new AjaxUtil({ + url : "/api/projects/" + projectId + "/logs/filter", + data: {}, + type: "post", + success: function(data){ + return data || []; + } + }).exec() + ).done(function(operationLogs){ + searchAccessLogCallback(operationLogs); + }); + } + + $("#btnSearchUser").on("click", function(){ + var username = $("#txtSearchUser").val(); + if($.trim(username).length == 0){ + username = null; + } + listUser(username); + }); + + function toUTCSeconds(date, hour, min, sec) { + var t = new Date(date); + t.setHours(hour); + t.setMinutes(min); + t.setSeconds(sec); + var utcTime = new Date(t.getUTCFullYear(), t.getUTCMonth(), t.getUTCDate(), t.getUTCHours(), t.getUTCMinutes(), t.getUTCSeconds()); - return utcTime.getTime() / 1000; - } - - $("#btnFilterLog").on("click", function(){ - - var projectId = $("#projectId").val(); - var username = $("#txtSearchUserName").val(); - - var beginTimestamp = 0; - var endTimestamp = 0; - - if($("#begindatepicker").val() != ""){ - beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0); - } - if($("#enddatepicker").val() != ""){ - endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59); - } - - new AjaxUtil({ - url: "/api/projects/" + projectId + "/logs/filter", - data:{"username":username, "project_id" : projectId, "keywords" : getKeyWords() , "beginTimestamp" : beginTimestamp, "endTimestamp" : endTimestamp}, - type: "post", - success: function(data, status, xhr){ - if(xhr && xhr.status == 200){ - searchAccessLogCallback(data); + return utcTime.getTime() / 1000; } - } - }).exec(); - }); - - $("#spnFilterOption input[name=chkAll]").on("click", function(){ - $("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked")); - }); - - $("#spnFilterOption input[name=chkOperation]").on("click", function(){ - if(!$(this).prop("checked")){ - $("#spnFilterOption input[name=chkAll]").prop("checked", false); - } - - var selectedAll = true; - - $("#spnFilterOption input[name=chkOperation]").each(function(i, e){ - if(!$(e).prop("checked")){ - selectedAll = false; - } - }); - - if(selectedAll){ - $("#spnFilterOption input[name=chkAll]").prop("checked", true); - } - }); - - function getKeyWords(){ - var keywords = ""; - var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked"); - var keywords = []; - $.each(checkedItemList, function(i, e){ - var itemValue = $(e).val(); - if(itemValue == "others" && $.trim($("#txtOthers").val()).length > 0){ - keywords.push($("#txtOthers").val()); - }else{ - keywords.push($(e).val()); - } - }); - return keywords.join("/"); - } - - $('#datetimepicker1').datetimepicker({ - locale: i18n.getLocale(), - ignoreReadonly: true, - format: 'L', - showClear: true - }); - $('#datetimepicker2').datetimepicker({ - locale: i18n.getLocale(), - ignoreReadonly: true, - format: 'L', - showClear: true - }); - }); - } - - $(document).on("keydown", function(e){ - if(e.keyCode == 13){ - e.preventDefault(); - if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){ - $("#btnSearchRepo").trigger("click"); - }else if($("#tabItemDetail li:eq(1)").is(":focus") || $("#txtSearchUser").is(":focus")){ - $("#btnSearchUser").trigger("click"); - }else if($("#tabItemDetail li:eq(2)").is(":focus") || $("#txtSearchUserName").is(":focus")){ - $("#btnFilterLog").trigger("click"); - }else if($("#txtUserName").is(":focus") || $("#lstRole :radio").is(":focus")){ - $("#btnSave").trigger("click"); - } + + $("#btnFilterLog").on("click", function(){ + + var projectId = $("#projectId").val(); + var username = $("#txtSearchUserName").val(); + + var beginTimestamp = 0; + var endTimestamp = 0; + + if($("#begindatepicker").val() != ""){ + beginTimestamp = toUTCSeconds($("#begindatepicker").val(), 0, 0, 0); + } + if($("#enddatepicker").val() != ""){ + endTimestamp = toUTCSeconds($("#enddatepicker").val(), 23, 59, 59); + } + + new AjaxUtil({ + url: "/api/projects/" + projectId + "/logs/filter", + data:{"username":username, "project_id" : Number(projectId), "keywords" : getKeyWords() , "begin_timestamp" : beginTimestamp, "end_timestamp" : endTimestamp}, + type: "post", + success: function(data, status, xhr){ + if(xhr && xhr.status == 200){ + searchAccessLogCallback(data); + } + } + }).exec(); + }); + + $("#spnFilterOption input[name=chkAll]").on("click", function(){ + $("#spnFilterOption input[name=chkOperation]").prop("checked", $(this).prop("checked")); + }); + + $("#spnFilterOption input[name=chkOperation]").on("click", function(){ + if(!$(this).prop("checked")){ + $("#spnFilterOption input[name=chkAll]").prop("checked", false); + } + + var selectedAll = true; + + $("#spnFilterOption input[name=chkOperation]").each(function(i, e){ + if(!$(e).prop("checked")){ + selectedAll = false; + } + }); + + if(selectedAll){ + $("#spnFilterOption input[name=chkAll]").prop("checked", true); + } + }); + + function getKeyWords(){ + var keywords = ""; + var checkedItemList=$("#spnFilterOption input[name=chkOperation]:checked"); + var keywords = []; + $.each(checkedItemList, function(i, e){ + var itemValue = $(e).val(); + if(itemValue == "others" && $.trim($("#txtOthers").val()).length > 0){ + keywords.push($("#txtOthers").val()); + }else{ + keywords.push($(e).val()); + } + }); + return keywords.join("/"); + } + + $('#datetimepicker1').datetimepicker({ + locale: i18n.getLocale(), + ignoreReadonly: true, + format: 'L', + showClear: true + }); + $('#datetimepicker2').datetimepicker({ + locale: i18n.getLocale(), + ignoreReadonly: true, + format: 'L', + showClear: true + }); + }); +} + +$(document).on("keydown", function(e){ + if(e.keyCode == 13){ + e.preventDefault(); + if($("#tabItemDetail li:eq(0)").is(":focus") || $("#txtRepoName").is(":focus")){ + $("#btnSearchRepo").trigger("click"); + }else if($("#tabItemDetail li:eq(1)").is(":focus") || $("#txtSearchUser").is(":focus")){ + $("#btnSearchUser").trigger("click"); + }else if($("#tabItemDetail li:eq(2)").is(":focus") || $("#txtSearchUserName").is(":focus")){ + $("#btnFilterLog").trigger("click"); + }else if($("#txtUserName").is(":focus") || $("#lstRole :radio").is(":focus")){ + $("#btnSave").trigger("click"); } - }); -}) + } +}); +}) \ No newline at end of file diff --git a/static/resources/js/login.js b/static/resources/js/login.js index 9b0bfda2b..21c0b6cd2 100644 --- a/static/resources/js/login.js +++ b/static/resources/js/login.js @@ -24,7 +24,7 @@ jQuery(function(){ }, error: function(jqXhr){ if(jqXhr.status == 401) - return false; + return false; } }).exec(); diff --git a/static/resources/js/project.js b/static/resources/js/project.js index 97da2d6f9..427aa39dd 100644 --- a/static/resources/js/project.js +++ b/static/resources/js/project.js @@ -13,13 +13,13 @@ limitations under the License. */ jQuery(function(){ - + new AjaxUtil({ url: "/api/users/current", type: "get", success: function(data, status, xhr){ if(xhr && xhr.status == 200){ - if(data.HasAdminRole == 1) { + if(data.has_admin_role == 1) { renderForAdminRole(); } renderForAnyRole(); @@ -29,57 +29,57 @@ jQuery(function(){ function renderForAnyRole(){ $("#tabProject a:first").tab("show"); - + $(document).on("keydown", function(e){ if(e.keyCode == 13){ - e.preventDefault(); - if($("#tabProject li:eq(0)").is(":focus") || $("#txtSearchProject").is(":focus")){ - $("#btnSearch").trigger("click"); - }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchPublicProjects").is(":focus")){ - $("#btnSearchPublicProjects").trigger("click"); - }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchUsername").is(":focus")){ - $("#btnSearchUsername").trigger("click"); - }else if($("#dlgAddProject").is(":focus") || $("#projectName").is(":focus")){ - $("#btnSave").trigger("click"); - } + e.preventDefault(); + if($("#tabProject li:eq(0)").is(":focus") || $("#txtSearchProject").is(":focus")){ + $("#btnSearch").trigger("click"); + }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchPublicProjects").is(":focus")){ + $("#btnSearchPublicProjects").trigger("click"); + }else if($("#tabProject li:eq(1)").is(":focus") || $("#txtSearchUsername").is(":focus")){ + $("#btnSearchUsername").trigger("click"); + }else if($("#dlgAddProject").is(":focus") || $("#projectName").is(":focus")){ + $("#btnSave").trigger("click"); + } } }); - + function listProject(projectName, isPublic){ currentPublic = isPublic; $.when( new AjaxUtil({ - url: "/api/projects?is_public=" + isPublic + "&project_name=" + (projectName == null ? "" : projectName) + "×tamp=" + new Date().getTime(), - type: "get", - success: function(data, status, xhr){ - $("#tblProject tbody tr").remove(); - $.each(data || [], function(i, e){ - var row = '' + - '' + e.Name + '' + - '' + moment(new Date(e.CreationTime)).format("YYYY-MM-DD HH:mm:ss") + ''; - if(e.Public == 1 && e.Togglable){ - row += '' - } else if (e.Public == 1) { - row += ''; - } else if (e.Public == 0 && e.Togglable) { - row += ''; - } else if (e.Public == 0) { - row += ''; - row += ''; - } - $("#tblProject tbody").append(row); - }); - } - }).exec()) + url: "/api/projects?is_public=" + isPublic + "&project_name=" + (projectName == null ? "" : projectName) + "×tamp=" + new Date().getTime(), + type: "get", + success: function(data, status, xhr){ + $("#tblProject tbody tr").remove(); + $.each(data || [], function(i, e){ + var row = '' + + '' + e.name + '' + + '' + moment(new Date(e.creation_time)).format("YYYY-MM-DD HH:mm:ss") + ''; + if(e.public == 1 && e.Togglable){ + row += '' + } else if (e.public == 1) { + row += ''; + } else if (e.public == 0 && e.Togglable) { + row += ''; + } else if (e.public == 0) { + row += ''; + row += ''; + } + $("#tblProject tbody").append(row); + }); + } + }).exec()) .done(function() { - $("#tblProject tbody tr :button").on("click", function(){ - var projectId = $(this).attr("projectid"); - var self = this; - new AjaxUtil({ - url: "/api/projects/" + projectId, - data: {"public": ($(self).hasClass("btn-success") ? false : true)}, - type: "put", - complete: function(jqXhr, status) { + $("#tblProject tbody tr :button").on("click", function(){ + var projectId = $(this).attr("projectid"); + var self = this; + new AjaxUtil({ + url: "/api/projects/" + projectId, + data: {"public": ($(self).hasClass("btn-success") ? false : true)}, + type: "put", + complete: function(jqXhr, status) { if($(self).hasClass("btn-success")){ $(self).removeClass("btn-success").addClass("btn-danger"); $(self).html(i18n.getMessage("button_off")); @@ -88,9 +88,9 @@ jQuery(function(){ $(self).html(i18n.getMessage("button_on")); } } - }).exec(); - }); - }); + }).exec(); + }); + }); } listProject(null, 0); var currentPublic = 0; @@ -119,7 +119,7 @@ jQuery(function(){ $("#projectName").val(""); $("#projectName").parent().addClass("has-feedback"); $("#projectName").siblings("span").removeClass("glyphicon-warning-sign").removeClass("glyphicon-ok"); - $("#isPublic").prop('checked', false); + $("#isPublic").prop('checked', false); }); $("#btnSave").on("click", function(){ @@ -161,52 +161,52 @@ jQuery(function(){ $("#tblUser tbody tr").remove(); $.each(data || [], function(i, e){ var row = '' + - '' + e.username + '' + - '' + e.email + ''; - if(e.HasAdminRole == 1){ - row += ''; + '' + e.username + '' + + '' + e.email + ''; + if(e.has_admin_role == 1){ + row += ''; } else { - row += ''; + row += ''; } - row += ''; + row += ''; row += ''; $("#tblUser tbody").append(row); }); } }).exec() - ).done(function(){ - $("#tblUser tbody tr :button").on("click",function(){ - var userId = $(this).attr("userid"); - var self = this; - new AjaxUtil({ - url: "/api/users/" + userId, - type: "put", - complete: function(jqXhr, status){ - if(jqXhr && jqXhr.status == 200){ - if($(self).hasClass("btn-success")){ - $(self).removeClass("btn-success").addClass("btn-danger"); - $(self).html(i18n.getMessage("button_off")); - }else{ - $(self).removeClass("btn-danger").addClass("btn-success"); - $(self).html(i18n.getMessage("button_on")); - } - } - } - }).exec(); - }); - $("#tblUser tbody tr").on("mouseover", function(){ - $(".tdDeleteUser", this).css({"visibility":"visible"}); - }).on("mouseout", function(){ - $(".tdDeleteUser", this).css({"visibility":"hidden"}); - }); - $("#tblUser tbody tr .tdDeleteUser").on("click", function(){ - var userId = $(this).attr("userid"); - $("#dlgModal") + ).done(function(){ + $("#tblUser tbody tr :button").on("click",function(){ + var userId = $(this).attr("userid"); + var self = this; + new AjaxUtil({ + url: "/api/users/" + userId, + type: "put", + complete: function(jqXhr, status){ + if(jqXhr && jqXhr.status == 200){ + if($(self).hasClass("btn-success")){ + $(self).removeClass("btn-success").addClass("btn-danger"); + $(self).html(i18n.getMessage("button_off")); + }else{ + $(self).removeClass("btn-danger").addClass("btn-success"); + $(self).html(i18n.getMessage("button_on")); + } + } + } + }).exec(); + }); + $("#tblUser tbody tr").on("mouseover", function(e){ + $(".tdDeleteUser", this).css({"visibility":"visible"}); + }).on("mouseout", function(e){ + $(".tdDeleteUser", this).css({"visibility":"hidden"}); + }); + $("#tblUser tbody tr .tdDeleteUser").on("click", function(e){ + var userId = $(this).attr("userid"); + $("#dlgModal") .dialogModal({ "title": i18n.getMessage("delete_user"), "content": i18n.getMessage("are_you_sure_to_delete_user") + $(this).attr("username") + " ?", "enableCancel": true, - "callback": function(){ + "callback": function(){ new AjaxUtil({ url: "/api/users/" + userId, type: "delete", @@ -218,17 +218,18 @@ jQuery(function(){ error: function(jqXhr){} }).exec(); } + }); + }); }); + } + listUserAdminRole(null); + $("#btnSearchUsername").on("click", function(){ + var username = $("#txtSearchUsername").val(); + if($.trim(username).length == 0){ + username = null; + } + listUserAdminRole(username); }); } - listUserAdminRole(null); - $("#btnSearchUsername").on("click", function(){ - var username = $("#txtSearchUsername").val(); - if($.trim(username).length == 0){ - username = null; - } - listUserAdminRole(username); - }); - } -}) + }) diff --git a/static/resources/js/register.js b/static/resources/js/register.js index 3fa2882b4..397087c01 100644 --- a/static/resources/js/register.js +++ b/static/resources/js/register.js @@ -30,14 +30,14 @@ jQuery(function(){ $("#btnPageSignUp").on("click", function(){ validateOptions.Validate(function() { - var username = $.trim($("#Username").val()); - var email = $.trim($("#Email").val()); - var password = $.trim($("#Password").val()); - var confirmedPassword = $.trim($("#ConfirmedPassword").val()); - var realname = $.trim($("#Realname").val()); - var comment = $.trim($("#Comment").val()); - var isAdmin = $("#isAdmin").val(); - + var username = $.trim($("#Username").val()); + var email = $.trim($("#Email").val()); + var password = $.trim($("#Password").val()); + var confirmedPassword = $.trim($("#ConfirmedPassword").val()); + var realname = $.trim($("#Realname").val()); + var comment = $.trim($("#Comment").val()); + var isAdmin = $("#isAdmin").val(); + new AjaxUtil({ url : "/api/users", data: {"username": username, "password": password, "realname": realname, "comment": comment, "email": email}, @@ -47,29 +47,29 @@ jQuery(function(){ }, error:function(jqxhr, status, error){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_sign_up"), - "content": i18n.getMessage("internal_error"), - "callback": function(){ - return; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_sign_up"), + "content": i18n.getMessage("internal_error"), + "callback": function(){ + return; + } + }); }, complete: function(xhr, status){ $("#btnPageSignUp").prop("disabled", false); if(xhr && xhr.status == 201){ $("#dlgModal") - .dialogModal({ - "title": isAdmin == "true" ? i18n.getMessage("title_add_user") : i18n.getMessage("title_sign_up"), - "content": isAdmin == "true" ? i18n.getMessage("added_user_successfully") : i18n.getMessage("registered_successfully"), - "callback": function(){ - if(isAdmin == "true") { - document.location = "/registry/project"; - }else{ - document.location = "/signIn"; - } + .dialogModal({ + "title": isAdmin == "true" ? i18n.getMessage("title_add_user") : i18n.getMessage("title_sign_up"), + "content": isAdmin == "true" ? i18n.getMessage("added_user_successfully") : i18n.getMessage("registered_successfully"), + "callback": function(){ + if(isAdmin == "true") { + document.location = "/registry/project"; + }else{ + document.location = "/signIn"; } - }); + } + }); } } }).exec(); diff --git a/static/resources/js/reset-password.js b/static/resources/js/reset-password.js index 0a3efc566..a6c504f48 100644 --- a/static/resources/js/reset-password.js +++ b/static/resources/js/reset-password.js @@ -17,11 +17,11 @@ jQuery(function(){ $("#Password,#ConfirmedPassword").on("blur", validateCallback); validateOptions.Items = ["#Password", "#ConfirmedPassword"]; - function bindEnterKey(){ + function bindEnterKey(){ $(document).on("keydown", function(e){ if(e.keyCode == 13){ - e.preventDefault(); - $("#btnSubmit").trigger("click"); + e.preventDefault(); + $("#btnSubmit").trigger("click"); } }); } @@ -30,7 +30,6 @@ jQuery(function(){ } bindEnterKey(); - var spinner = new Spinner({scale:1}).spin(); $("#btnSubmit").on("click", function(){ @@ -42,20 +41,20 @@ jQuery(function(){ "type": "post", "data": {"reset_uuid": resetUuid, "password": password}, "beforeSend": function(e){ - unbindEnterKey(); - $("h1").append(spinner.el); - $("#btnSubmit").prop("disabled", true); + unbindEnterKey(); + $("h1").append(spinner.el); + $("#btnSubmit").prop("disabled", true); }, "success": function(data, status, xhr){ if(xhr && xhr.status == 200){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_reset_password"), - "content": i18n.getMessage("reset_password_successfully"), - "callback": function(){ - document.location="/signIn"; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_reset_password"), + "content": i18n.getMessage("reset_password_successfully"), + "callback": function(){ + document.location="/signIn"; + } + }); } }, @@ -66,14 +65,14 @@ jQuery(function(){ "error": function(jqXhr, status, error){ if(jqXhr){ $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_reset_password"), - "content": i18n.getMessage(jqXhr.responseText), - "callback": function(){ - bindEnterKey(); - return; - } - }); + .dialogModal({ + "title": i18n.getMessage("title_reset_password"), + "content": i18n.getMessage(jqXhr.responseText), + "callback": function(){ + bindEnterKey(); + return; + } + }); } } }); diff --git a/static/resources/js/search.js b/static/resources/js/search.js index 5284866f9..e0158baa8 100644 --- a/static/resources/js/search.js +++ b/static/resources/js/search.js @@ -60,12 +60,12 @@ jQuery(function(){ $.each(data, function(i, e){ var project, description, repoName; switch(discriminator){ - case "project": + case "project": project = new Project(e.id, e.name, e.public); description = project.name; repoName = ""; break; - case "repository": + case "repository": project = new Project(e.project_id, e.project_name, e.project_public); description = e.repository_name; repoName = e.repository_name.substring(e.repository_name.lastIndexOf("/") + 1); diff --git a/static/resources/js/sign-in.js b/static/resources/js/sign-in.js index 1602a6b04..2aaa24c5f 100644 --- a/static/resources/js/sign-in.js +++ b/static/resources/js/sign-in.js @@ -56,7 +56,7 @@ jQuery(function(){ success: function(jqXhr, status){ var lastUri = location.search; if(lastUri != "" && lastUri.indexOf("=") > 0){ - document.location = decodeURIComponent(lastUri.split("=")[1]); + document.location = decodeURIComponent(lastUri.split("=")[1]); }else{ document.location = "/registry/project"; } @@ -69,10 +69,10 @@ jQuery(function(){ i18nKey = "check_your_username_or_password" } $("#dlgModal") - .dialogModal({ - "title": i18n.getMessage("title_login_failed"), - "content": i18n.getMessage(i18nKey) - }); + .dialogModal({ + "title": i18n.getMessage("title_login_failed"), + "content": i18n.getMessage(i18nKey) + }); } }); }); diff --git a/static/resources/js/validate-options.js b/static/resources/js/validate-options.js index 34f28ead9..492366ca7 100644 --- a/static/resources/js/validate-options.js +++ b/static/resources/js/validate-options.js @@ -26,18 +26,18 @@ var validateOptions = { "Username" :{ "Required": { "value" : true, "errMsg" : i18n.getMessage("username_is_required")}, "CheckExist": { "value" : function(value){ - var result = true; - $.ajax({ - url: "/userExists", - data: {"target": "username", "value" : value}, + var result = true; + $.ajax({ + url: "/userExists", + data: {"target": "username", "value" : value}, dataType: "json", - type: "post", - async: false, - success: function(data){ - result = data; - } - }); - return result; + type: "post", + async: false, + success: function(data){ + result = data; + } + }); + return result; }, "errMsg" : i18n.getMessage("username_has_been_taken")}, "MaxLength": {"value" : 20, "errMsg" : i18n.getMessage("username_is_too_long")}, "IllegalChar": {"value": [",","~","#", "$", "%"] , "errMsg": i18n.getMessage("username_contains_illegal_chars")} @@ -45,40 +45,40 @@ var validateOptions = { "Email" :{ "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")}, "RegExp": {"value": /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, - "errMsg": i18n.getMessage("email_contains_illegal_chars")}, + "errMsg": i18n.getMessage("email_contains_illegal_chars")}, "CheckExist": { "value" : function(value){ - var result = true; - $.ajax({ - url: "/userExists", - data: {"target": "email", "value": value}, - dataType: "json", - type: "post", - async: false, - success: function(data){ - result = data; - } - }); - return result; - }, "errMsg" : i18n.getMessage("email_has_been_taken")} + var result = true; + $.ajax({ + url: "/userExists", + data: {"target": "email", "value": value}, + dataType: "json", + type: "post", + async: false, + success: function(data){ + result = data; + } + }); + return result; + }, "errMsg" : i18n.getMessage("email_has_been_taken")} }, "EmailF" :{ "Required": { "value" : true, "errMsg" : i18n.getMessage("email_is_required")}, "RegExp": {"value": /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, - "errMsg": i18n.getMessage("email_content_illegal")}, + "errMsg": i18n.getMessage("email_content_illegal")}, "CheckIfNotExist": { "value" : function(value){ - var result = true; - $.ajax({ - url: "/userExists", - data: {"target": "email", "value": value}, - dataType: "json", - type: "post", - async: false, - success: function(data){ - result = data; - } - }); - return result; - }, "errMsg" : i18n.getMessage("email_does_not_exist")} + var result = true; + $.ajax({ + url: "/userExists", + data: {"target": "email", "value": value}, + dataType: "json", + type: "post", + async: false, + success: function(data){ + result = data; + } + }); + return result; + }, "errMsg" : i18n.getMessage("email_does_not_exist")} }, "Realname" :{ "Required": { "value" : true, "errMsg" : i18n.getMessage("realname_is_required")}, @@ -119,7 +119,7 @@ function validateCallback(target){ var currentId = $(target).attr("id"); var validateItem = validateOptions[currentId]; - var errMsg = ""; + var errMsg = ""; for(var checkTitle in validateItem){ diff --git a/tests/apitests/apilib/accesslog.go b/tests/apitests/apilib/accesslog.go new file mode 100644 index 000000000..0d6318102 --- /dev/null +++ b/tests/apitests/apilib/accesslog.go @@ -0,0 +1,23 @@ +/* + Copyright (c) 2016 VMware, Inc. All Rights Reserved. + 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 HarborAPI + +type AccessLog struct { + Username string `json:"username,omitempty"` + Keywords string `json:"keywords,omitempty"` + BeginTimestamp int32 `json:"beginTimestamp,omitempty"` + EndTimestamp int32 `json:"endTimestamp,omitempty"` +} diff --git a/tests/apitests/apilib/harborapi.go b/tests/apitests/apilib/harborapi.go new file mode 100644 index 000000000..ee0d0d9e1 --- /dev/null +++ b/tests/apitests/apilib/harborapi.go @@ -0,0 +1,113 @@ +//Package HarborAPI +//These APIs provide services for manipulating Harbor project. +package HarborAPI + +import ( + "encoding/json" + //"fmt" + "io/ioutil" + "net/http" + + "github.com/dghubble/sling" +) + +type HarborAPI struct { + basePath string +} + +func NewHarborAPI() *HarborAPI { + return &HarborAPI{ + basePath: "http://localhost", + } +} + +func NewHarborAPIWithBasePath(basePath string) *HarborAPI { + return &HarborAPI{ + basePath: basePath, + } +} + +type UsrInfo struct { + Name string + Passwd string +} + +//Search for projects and repositories +//Implementation Notes +//The Search endpoint returns information about the projects and repositories +//offered at public status or related to the current logged in user. +//The response includes the project and repository list in a proper display order. +//@param q Search parameter for project and repository name. +//@return []Search +//func (a HarborAPI) SearchGet (q string) (Search, error) { +func (a HarborAPI) SearchGet(q string) (Search, error) { + + _sling := sling.New().Get(a.basePath) + + // create path and map variables + path := "/api/search" + + _sling = _sling.Path(path) + + type QueryParams struct { + Query string `url:"q"` + } + + _sling = _sling.QueryStruct(&QueryParams{q}) + + // accept header + accepts := []string{"application/json", "text/plain"} + for key := range accepts { + _sling = _sling.Set("Accept", accepts[key]) + break // only use the first Accept + } + + req, err := _sling.Request() + client := &http.Client{} + httpResponse, err := client.Do(req) + defer httpResponse.Body.Close() + + body, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + // handle error + } + + var successPayload = new(Search) + err = json.Unmarshal(body, &successPayload) + return *successPayload, err +} + +//Create a new project. +//Implementation Notes +//This endpoint is for user to create a new project. +//@param project New created project. +//@return void +//func (a HarborAPI) ProjectsPost (prjUsr UsrInfo, project Project) (int, error) { +func (a HarborAPI) ProjectsPost(prjUsr UsrInfo, project Project) (int, error) { + + _sling := sling.New().Post(a.basePath) + + // create path and map variables + path := "/api/projects" + + _sling = _sling.Path(path) + + // accept header + accepts := []string{"application/json", "text/plain"} + for key := range accepts { + _sling = _sling.Set("Accept", accepts[key]) + break // only use the first Accept + } + + // body params + _sling = _sling.BodyJSON(project) + + req, err := _sling.Request() + req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd) + + client := &http.Client{} + httpResponse, err := client.Do(req) + defer httpResponse.Body.Close() + + return httpResponse.StatusCode, err +} diff --git a/tests/apitests/apilib/harborlogout.go b/tests/apitests/apilib/harborlogout.go new file mode 100644 index 000000000..fa59ee2bb --- /dev/null +++ b/tests/apitests/apilib/harborlogout.go @@ -0,0 +1,15 @@ +// HarborLogout.go +package HarborAPI + +import ( + "net/http" +) + +func (a HarborAPI) HarborLogout() (int, error) { + + response, err := http.Get(a.basePath + "/logout") + + defer response.Body.Close() + + return response.StatusCode, err +} diff --git a/tests/apitests/apilib/harlogin.go b/tests/apitests/apilib/harlogin.go new file mode 100644 index 000000000..d711103bb --- /dev/null +++ b/tests/apitests/apilib/harlogin.go @@ -0,0 +1,28 @@ +// HarborLogon.go +package HarborAPI + +import ( + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +func (a HarborAPI) HarborLogin(user UsrInfo) (int, error) { + + v := url.Values{} + v.Set("principal", user.Name) + v.Set("password", user.Passwd) + + body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce] + + client := &http.Client{} + reqest, err := http.NewRequest("POST", a.basePath+"/login", body) + + reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head + + resp, err := client.Do(reqest) + defer resp.Body.Close() //close resp.Body + + return resp.StatusCode, err +} diff --git a/tests/apitests/apilib/project.go b/tests/apitests/apilib/project.go new file mode 100644 index 000000000..19444957b --- /dev/null +++ b/tests/apitests/apilib/project.go @@ -0,0 +1,15 @@ +package HarborAPI + +import () + +type Project struct { + ProjectId int32 `json:"id,omitempty"` + OwnerId int32 `json:"owner_id,omitempty"` + ProjectName string `json:"project_name,omitempty"` + CreationTime string `json:"creation_time,omitempty"` + Deleted int32 `json:"deleted,omitempty"` + UserId int32 `json:"user_id,omitempty"` + OwnerName string `json:"owner_name,omitempty"` + Public bool `json:"public,omitempty"` + Togglable bool `json:"togglable,omitempty"` +} diff --git a/tests/apitests/apilib/projecttemp4search.go b/tests/apitests/apilib/projecttemp4search.go new file mode 100644 index 000000000..c7219cc97 --- /dev/null +++ b/tests/apitests/apilib/projecttemp4search.go @@ -0,0 +1,9 @@ +package HarborAPI + +import () + +type Project4Search struct { + ProjectId int32 `json:"id,omitempty"` + ProjectName string `json:"name,omitempty"` + Public int32 `json:"public,omitempty"` +} diff --git a/tests/apitests/apilib/repository.go b/tests/apitests/apilib/repository.go new file mode 100644 index 000000000..5de9302df --- /dev/null +++ b/tests/apitests/apilib/repository.go @@ -0,0 +1,16 @@ +package HarborAPI + +import ( + "time" +) + +type Repository struct { + Id string `json:"id,omitempty"` + Parent string `json:"parent,omitempty"` + Created time.Time `json:"created,omitempty"` + DurationDays string `json:"duration_days,omitempty"` + Author string `json:"author,omitempty"` + Architecture string `json:"architecture,omitempty"` + DockerVersion string `json:"docker_version,omitempty"` + Os string `json:"os,omitempty"` +} diff --git a/tests/apitests/apilib/repositorytemp4search.go b/tests/apitests/apilib/repositorytemp4search.go new file mode 100644 index 000000000..f0dead98b --- /dev/null +++ b/tests/apitests/apilib/repositorytemp4search.go @@ -0,0 +1,9 @@ +package HarborAPI + +type Repository4Search struct { + ProjectId int32 `json:"project_id,omitempty"` + ProjectName string `json:"project_name,omitempty"` + ProjectPublic int32 `json:"project_public,omitempty"` + RepoName string `json:"repository_name,omitempty"` +} + diff --git a/tests/apitests/apilib/role.go b/tests/apitests/apilib/role.go new file mode 100644 index 000000000..c6e53b044 --- /dev/null +++ b/tests/apitests/apilib/role.go @@ -0,0 +1,7 @@ +package HarborAPI + +type Role struct { + RoleId int32 `json:"role_id,omitempty"` + RoleCode string `json:"role_code,omitempty"` + RoleName string `json:"role_name,omitempty"` +} diff --git a/tests/apitests/apilib/roleparam.go b/tests/apitests/apilib/roleparam.go new file mode 100644 index 000000000..12cf755a5 --- /dev/null +++ b/tests/apitests/apilib/roleparam.go @@ -0,0 +1,6 @@ +package HarborAPI + +type RoleParam struct { + Roles []int32 `json:"roles,omitempty"` + UserName string `json:"user_name,omitempty"` +} diff --git a/tests/apitests/apilib/search.go b/tests/apitests/apilib/search.go new file mode 100644 index 000000000..81ca0fb20 --- /dev/null +++ b/tests/apitests/apilib/search.go @@ -0,0 +1,8 @@ +package HarborAPI + +import () + +type Search struct { + Projects []Project4Search `json:"project,omitempty"` + Repositories []Repository4Search `json:"repository,omitempty"` +} diff --git a/tests/apitests/apilib/user.go b/tests/apitests/apilib/user.go new file mode 100644 index 000000000..10a07933d --- /dev/null +++ b/tests/apitests/apilib/user.go @@ -0,0 +1,11 @@ +package HarborAPI + +type User struct { + UserId int32 `json:"user_id,omitempty"` + Username string `json:"username,omitempty"` + Email string `json:"email,omitempty"` + Password string `json:"password,omitempty"` + Realname string `json:"realname,omitempty"` + Comment string `json:"comment,omitempty"` + Deleted int32 `json:"deleted,omitempty"` +} diff --git a/tests/apitests/hbapiaddprj_test.go b/tests/apitests/hbapiaddprj_test.go new file mode 100644 index 000000000..4bb73ed8c --- /dev/null +++ b/tests/apitests/hbapiaddprj_test.go @@ -0,0 +1,95 @@ +package HarborAPItest + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" + "github.com/vmware/harbor/tests/apitests/apilib" +) + +func TestAddProject(t *testing.T) { + + fmt.Println("Test for Project Add (ProjectsPost) API\n") + assert := assert.New(t) + + apiTest := HarborAPI.NewHarborAPI() + + //prepare for test + adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"} + admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"} + + prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"} + + var project HarborAPI.Project + project.ProjectName = "testProject" + project.Public = true + + //case 1: admin login fail, expect project creation fail. + fmt.Println("case 1: admin login fail, expect project creation fail.") + resault, err := apiTest.HarborLogin(*adminEr) + if err != nil { + t.Error("Error while admin login", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(401), "Admin login status should be 401") + //t.Log(resault) + } + + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(401), "Case 1: Project creation status should be 401") + //t.Log(resault) + } + + //case 2: admin successful login, expect project creation success. + fmt.Println("case 2: admin successful login, expect project creation success.") + resault, err = apiTest.HarborLogin(*admin) + if err != nil { + t.Error("Error while admin login", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(200), "Admin login status should be 200") + //t.Log(resault) + } + if resault != 200 { + t.Log(resault) + } else { + prjUsr = admin + } + + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(201), "Case 2: Project creation status should be 201") + //t.Log(resault) + } + + //case 3: duplicate project name, create project fail + fmt.Println("case 3: duplicate project name, create project fail") + resault, err = apiTest.ProjectsPost(*prjUsr, project) + if err != nil { + t.Error("Error while creat project", err.Error()) + t.Log(err) + } else { + assert.Equal(resault, int(409), "Case 3: Project creation status should be 409") + //t.Log(resault) + } + + //resault1, err := apiTest.HarborLogout() + //if err != nil { + // t.Error("Error while admin logout", err.Error()) + // t.Log(err) + //} else { + // assert.Equal(resault1, int(200), "Admin logout status") + // //t.Log(resault) + //} + //if resault1 != 200 { + // t.Log(resault) + //} + +} diff --git a/tests/apitests/hbapisearch_test.go b/tests/apitests/hbapisearch_test.go new file mode 100644 index 000000000..1d449d408 --- /dev/null +++ b/tests/apitests/hbapisearch_test.go @@ -0,0 +1,31 @@ +package HarborAPItest + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "testing" + "github.com/vmware/harbor/tests/apitests/apilib" +) + +func TestSearch(t *testing.T) { + fmt.Println("Test for Search (SearchGet) API\n") + assert := assert.New(t) + + apiTest := HarborAPI.NewHarborAPI() + var resault HarborAPI.Search + resault, err := apiTest.SearchGet("library") + //fmt.Printf("%+v\n", resault) + if err != nil { + t.Error("Error while search project or repository", err.Error()) + t.Log(err) + } else { + assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal") + assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library") + assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)") + //t.Log(resault) + } + //if resault.Response.StatusCode != 200 { + // t.Log(resault.Response) + //} + +} diff --git a/tests/hostcfg.sh b/tests/hostcfg.sh new file mode 100755 index 000000000..d25c9dfec --- /dev/null +++ b/tests/hostcfg.sh @@ -0,0 +1,4 @@ +#!/bin/bash +IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'` +#echo $IP +sed "s/reg.mydomain.com/$IP/" -i Deploy/harbor.cfg diff --git a/tests/startuptest.go b/tests/startuptest.go new file mode 100644 index 000000000..f611ef58c --- /dev/null +++ b/tests/startuptest.go @@ -0,0 +1,36 @@ +// Fetch prints the content found at a URL. +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "strings" + "time" +) + +func main() { + time.Sleep(60*time.Second) + for _, url := range os.Args[1:] { + resp, err := http.Get(url) + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: %v\n", err) + os.Exit(1) + } + b, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err) + os.Exit(1) + } +// fmt.Printf("%s", b) + if strings.Contains(string(b), "Harbor") { + fmt.Printf("sucess!\n") + } else { + os.Exit(1) + } + + } +} + diff --git a/tests/testprepare.sh b/tests/testprepare.sh new file mode 100755 index 000000000..0d9a30ce2 --- /dev/null +++ b/tests/testprepare.sh @@ -0,0 +1,11 @@ +IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'` +#echo $IP +docker pull hello-world +docker pull docker +#docker login -u admin -p Harbor12345 $IP + +docker tag hello-world $IP/library/hello-world +docker push $IP/library/hello-world + +docker tag docker $IP/library/docker +docker push $IP/library/docker diff --git a/tests/userlogintest.go b/tests/userlogintest.go new file mode 100644 index 000000000..3e8303119 --- /dev/null +++ b/tests/userlogintest.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + "flag" +) + +func main() { + usrNamePtr := flag.String("name","anaymous","user name") + usrPasswdPtr := flag.String("passwd","anaymous","user password") + flag.Parse() + + v := url.Values{} + v.Set("principal", *usrNamePtr) + v.Set("password", *usrPasswdPtr) + + body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce] + fmt.Println(v) + + client := &http.Client{} + reqest, err := http.NewRequest("POST", "http://localhost/login", body) + if err != nil { + fmt.Println("Fatal error ", err.Error()) + } + + reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head + + resp, err := client.Do(reqest) + defer resp.Body.Close() //close resp.Body + + fmt.Println("login status: ", resp.StatusCode) //print status code + + //content_post, err := ioutil.ReadAll(resp.Body) + //if err != nil { + // fmt.Println("Fatal error ", err.Error()) + //} + + //fmt.Println(string(content_post)) //print reply + + response, err := http.Get("http://localhost/api/logout") + if err != nil { + fmt.Println("Fatal error ", err.Error()) + } + + defer response.Body.Close() + + fmt.Println("logout status: ", resp.StatusCode) //print status code + //content_get, err := ioutil.ReadAll(response.Body) + //fmt.Println(string(content_get)) + +} diff --git a/views/change-password.tpl b/views/change-password.tpl index cdcbdb9a5..9dfa50494 100644 --- a/views/change-password.tpl +++ b/views/change-password.tpl @@ -16,32 +16,32 @@
- -
- - - -
-
- - - -
{{i18n .Lang "password_description"}}
-
-
- - - -
{{i18n .Lang "password_description"}}
-
-
-
- -
-
+ +
+ + + +
+
+ + + +
{{i18n .Lang "password_description"}}
+
+
+ + + +
{{i18n .Lang "password_description"}}
+
+
+
+ +
+
diff --git a/views/forgot-password.tpl b/views/forgot-password.tpl index 64f8656b7..0a2738479 100644 --- a/views/forgot-password.tpl +++ b/views/forgot-password.tpl @@ -19,19 +19,19 @@

{{i18n .Lang "title_forgot_password"}}

-
- -
- - - -
{{i18n .Lang "forgot_password_description"}}
-
-
-
- -
-
+
+ +
+ + + +
{{i18n .Lang "forgot_password_description"}}
+
+
+
+ +
+
diff --git a/views/index.tpl b/views/index.tpl index e92eaa5cb..dec019fb1 100644 --- a/views/index.tpl +++ b/views/index.tpl @@ -13,25 +13,25 @@ limitations under the License. --> -
-
- Harbor's Logo -

{{i18n .Lang "index_title"}}

-
-
+
+
+ Harbor's Logo +

{{i18n .Lang "index_title"}}

+
+
-
- -
-
-

{{i18n .Lang "index_desc"}}

-

{{i18n .Lang "index_desc_0"}}

-

{{i18n .Lang "index_desc_1"}}

-

{{i18n .Lang "index_desc_2"}}

-

{{i18n .Lang "index_desc_3"}}

-

{{i18n .Lang "index_desc_4"}}

-

{{i18n .Lang "index_desc_5"}}

-
-
-
+
+ +
+
+

{{i18n .Lang "index_desc"}}

+

{{i18n .Lang "index_desc_0"}}

+

{{i18n .Lang "index_desc_1"}}

+

{{i18n .Lang "index_desc_2"}}

+

{{i18n .Lang "index_desc_3"}}

+

{{i18n .Lang "index_desc_4"}}

+

{{i18n .Lang "index_desc_5"}}

+
+
+
\ No newline at end of file diff --git a/views/item-detail.tpl b/views/item-detail.tpl index e9cdb2753..cce2c2aa3 100644 --- a/views/item-detail.tpl +++ b/views/item-detail.tpl @@ -18,7 +18,7 @@
  • {{.ProjectName}}
  • @@ -29,152 +29,150 @@
  • {{i18n .Lang "logs"}}
  • -
    - - - - - - - - - -
    -
    -
    -
    - -
    -
    {{i18n .Lang "repo_name"}}:
    - - - - -
    -
    -
    -

    -

    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    {{i18n .Lang "username"}}:
    - +
    + + + + + + + + + +
    +
    + +
    + +
    +
    {{i18n .Lang "repo_name"}}:
    + - +
    + +

    +

    + +
    +
    - - -

    -

    - - - - - - - - - - -
    {{i18n .Lang "username"}}{{i18n .Lang "role"}}{{i18n .Lang "operation"}}
    -
    -
    -
    -
    - -
    -
    {{i18n .Lang "username"}}:
    - - - - -
    -
    -
    -
    - -
    -
    - -

    -
    - +
    +
    - +
    + +
    +
    {{i18n .Lang "username"}}:
    + + + + +
    +
    +
    + + +

    +

    + + + + + + + + + + +
    {{i18n .Lang "username"}}{{i18n .Lang "role"}}{{i18n .Lang "operation"}}
    +
    +
    +
    +
    +
    +
    -
    {{i18n .Lang "operation"}}:
    - - {{i18n .Lang "all"}} - Create - Pull - Push - Delete - {{i18n .Lang "others"}}: - +
    {{i18n .Lang "username"}}:
    + + +
    -

    - -
    -
    {{i18n .Lang "start_date"}}:
    -
    - - - - -
    +
    +
    -
    -
    -
    -
    {{i18n .Lang "end_date"}}:
    -
    - - - - -
    -
    - -
    - -
    -
    - - - - - - - - - - - - -
    {{i18n .Lang "username"}}{{i18n .Lang "repo_name"}}{{i18n .Lang "repo_tag"}}{{i18n .Lang "operation"}}{{i18n .Lang "timestamp"}}
    -
    -
    +

    +

    +
    +
    + +
    +
    {{i18n .Lang "operation"}}:
    + + {{i18n .Lang "all"}} + Create + Pull + Push + Delete + {{i18n .Lang "others"}}: + + +
    +
    +

    +
    + +
    +
    {{i18n .Lang "start_date"}}:
    +
    + + + + +
    +
    +
    +
    +
    +
    {{i18n .Lang "end_date"}}:
    +
    + + + + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + +
    {{i18n .Lang "username"}}{{i18n .Lang "repo_name"}}{{i18n .Lang "repo_tag"}}{{i18n .Lang "operation"}}{{i18n .Lang "timestamp"}}
    +
    + +
    +
    -
    + {{ if .Username }} +
    + +
    + {{ else if eq .AuthMode "db_auth" }} +
    +   + {{ if eq .SelfRegistration true }} +   + {{ end }} +
    + {{ else }} +
    +   +
    {{ end }} -
    - {{ else }} -
    -   -
    - {{ end }} - -
    - + +
    + + diff --git a/views/segment/modal-dialog.tpl b/views/segment/modal-dialog.tpl index 95a220b69..7f3da1215 100644 --- a/views/segment/modal-dialog.tpl +++ b/views/segment/modal-dialog.tpl @@ -13,26 +13,26 @@ limitations under the License. --> \ No newline at end of file diff --git a/views/sign-in.tpl b/views/sign-in.tpl index 8cb173d27..86723ad01 100644 --- a/views/sign-in.tpl +++ b/views/sign-in.tpl @@ -13,28 +13,28 @@ limitations under the License. -->
    -