updated repo struct

This commit is contained in:
Brad Rydzewski 2015-06-18 18:50:57 -07:00
parent 564e029c17
commit 2b269d0e54
9 changed files with 281 additions and 316 deletions

View file

@ -108,10 +108,8 @@ func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error)
repo.Link = *repo_.HTMLURL
repo.Private = *repo_.Private
repo.Clone = *repo_.CloneURL
repo.Branch = "master"
if repo_.Language != nil {
repo.Language = *repo_.Language
}
if repo_.DefaultBranch != nil {
repo.Branch = *repo_.DefaultBranch
}
@ -264,7 +262,6 @@ func (g *GitHub) push(r *http.Request) (*common.Hook, error) {
repo.Link = hook.Repo.HTMLURL
repo.Private = hook.Repo.Private
repo.Clone = hook.Repo.CloneURL
repo.Language = hook.Repo.Language
repo.Branch = hook.Repo.DefaultBranch
commit := &common.Commit{}
@ -315,9 +312,7 @@ func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) {
repo.Link = *hook.Repo.HTMLURL
repo.Private = *hook.Repo.Private
repo.Clone = *hook.Repo.CloneURL
if hook.Repo.Language != nil {
repo.Language = *hook.Repo.Language
}
repo.Branch = "master"
if hook.Repo.DefaultBranch != nil {
repo.Branch = *hook.Repo.DefaultBranch
}

View file

@ -132,11 +132,6 @@ func RunBuild(c *gin.Context) {
return
}
keys := &common.Keypair{
Public: repo.PublicKey,
Private: repo.PrivateKey,
}
user, err := store.User(repo.UserID)
if err != nil {
c.Fail(404, err)
@ -189,7 +184,7 @@ func RunBuild(c *gin.Context) {
User: user,
Repo: repo,
Commit: commit,
Keys: keys,
Keys: repo.Keys,
Netrc: netrc,
Yaml: raw,
Plugins: conf.Plugins,

View file

@ -68,11 +68,11 @@ func PostHook(c *gin.Context) {
log.Warnf("ignoring hook. repo %s has no owner.", repo.FullName)
c.Writer.WriteHeader(204)
return
case !repo.PostCommit && hook.Commit.PullRequest != "":
case !repo.Hooks.Push && hook.Commit.PullRequest != "":
log.Infof("ignoring hook. repo %s is disabled.", repo.FullName)
c.Writer.WriteHeader(204)
return
case !repo.PullRequest && hook.Commit.PullRequest == "":
case !repo.Hooks.PullRequest && hook.Commit.PullRequest == "":
log.Warnf("ignoring hook. repo %s is disabled for pull requests.", repo.FullName)
c.Writer.WriteHeader(204)
return
@ -117,10 +117,6 @@ func PostHook(c *gin.Context) {
Environment: axis,
})
}
keys := &common.Keypair{
Public: repo.PublicKey,
Private: repo.PrivateKey,
}
netrc, err := remote.Netrc(user)
if err != nil {
@ -155,7 +151,7 @@ func PostHook(c *gin.Context) {
User: user,
Repo: repo,
Commit: commit,
Keys: keys,
Keys: repo.Keys,
Netrc: netrc,
Yaml: raw,
Plugins: conf.Plugins,

View file

@ -66,9 +66,7 @@ func GetRepo(c *gin.Context) {
// and keypair data.
if perm.Push {
data.Params = repo.Params
data.Keypair = &common.Keypair{
Public: repo.PublicKey,
}
data.Keypair = repo.Keys
}
// check to see if the user is subscribing to the repo
data.Starred, _ = store.Starred(user, repo)
@ -98,10 +96,10 @@ func PutRepo(c *gin.Context) {
}
if in.PostCommit != nil {
repo.PostCommit = *in.PostCommit
repo.Hooks.Push = *in.PostCommit
}
if in.PullRequest != nil {
repo.PullRequest = *in.PullRequest
repo.Hooks.PullRequest = *in.PullRequest
}
if in.Trusted != nil && user.Admin {
repo.Trusted = *in.Trusted
@ -118,9 +116,7 @@ func PutRepo(c *gin.Context) {
data := repoResp{repo, perm, nil, nil, false}
data.Params = repo.Params
data.Keypair = &common.Keypair{
Public: repo.PublicKey,
}
data.Keypair = repo.Keys
data.Starred, _ = store.Starred(user, repo)
c.JSON(200, data)
@ -208,8 +204,9 @@ func PostRepo(c *gin.Context) {
// set the repository owner to the
// currently authenticated user.
r.UserID = user.ID
r.PostCommit = true
r.PullRequest = true
r.Hooks = new(common.Hooks)
r.Hooks.Push = true
r.Hooks.PullRequest = true
r.Timeout = 60 // 1 hour default build time
r.Self = fmt.Sprintf(
"%s/%s",
@ -223,16 +220,13 @@ func PostRepo(c *gin.Context) {
c.Fail(400, err)
return
}
r.PublicKey = string(sshutil.MarshalPublicKey(&key.PublicKey))
r.PrivateKey = string(sshutil.MarshalPrivateKey(key))
keypair := &common.Keypair{
Public: r.PublicKey,
Private: r.PrivateKey,
}
r.Keys = new(common.Keypair)
r.Keys.Public = string(sshutil.MarshalPublicKey(&key.PublicKey))
r.Keys.Private = string(sshutil.MarshalPrivateKey(key))
// activate the repository before we make any
// local changes to the database.
err = remote.Activate(user, r, keypair, link)
err = remote.Activate(user, r, r.Keys, link)
if err != nil {
c.Fail(500, err)
return

View file

@ -52,22 +52,20 @@ CREATE TABLE IF NOT EXISTS repos (
,repo_owner VARCHAR(255)
,repo_name VARCHAR(255)
,repo_full_name VARCHAR(1024)
,repo_token VARCHAR(255)
,repo_language VARCHAR(255)
,repo_branch VARCHAR(255)
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_self VARCHAR(1024)
,repo_link VARCHAR(1024)
,repo_clone VARCHAR(1024)
,repo_post_commit BOOLEAN
,repo_pull_request BOOLEAN
,repo_public_key BLOB
,repo_private_key BLOB
,repo_params BLOB
,repo_branch VARCHAR(255)
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_timeout INTEGER
,repo_created INTEGER
,repo_updated INTEGER
,repo_keys_public BLOB
,repo_keys_private BLOB
,repo_hooks_pull_request BOOLEAN
,repo_hooks_push BOOLEAN
,repo_hooks_tags BOOLEAN
,repo_params BLOB
,UNIQUE(repo_owner, repo_name)
,UNIQUE(repo_full_name)
);

View file

@ -2,7 +2,6 @@ package builtin
import (
"database/sql"
"time"
"github.com/drone/drone/pkg/types"
)
@ -35,14 +34,11 @@ func (db *Repostore) RepoList(user *types.User) ([]*types.Repo, error) {
// AddRepo inserts a repo in the datastore.
func (db *Repostore) AddRepo(repo *types.Repo) error {
repo.Created = time.Now().UTC().Unix()
repo.Updated = time.Now().UTC().Unix()
return createRepo(db, rebind(stmtRepoInsert), repo)
}
// SetRepo updates a repo in the datastore.
func (db *Repostore) SetRepo(repo *types.Repo) error {
repo.Updated = time.Now().UTC().Unix()
return updateRepo(db, rebind(stmtRepoUpdate), repo)
}
@ -56,27 +52,24 @@ func (db *Repostore) DelRepo(repo *types.Repo) error {
// with permissions for the given User ID.
const repoListQuery = `
SELECT
r.repo_id
,r.repo_user_id
,r.repo_owner
,r.repo_name
,r.repo_full_name
,r.repo_token
,r.repo_language
,r.repo_private
,r.repo_self
,r.repo_link
,r.repo_clone
,r.repo_branch
,r.repo_timeout
,r.repo_trusted
,r.repo_post_commit
,r.repo_pull_request
,r.repo_public_key
,r.repo_private_key
,r.repo_created
,r.repo_updated
,r.repo_params
repo_id
,repo_user_id
,repo_owner
,repo_name
,repo_full_name
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_private
,repo_trusted
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM
repos r
,stars s

View file

@ -40,40 +40,38 @@ func createRepo(db repoDB, query string, v *Repo) error {
var v3 string
var v4 string
var v5 string
var v6 bool
var v6 string
var v7 string
var v8 string
var v9 string
var v10 string
var v11 int64
var v12 bool
var v8 bool
var v9 bool
var v10 int64
var v11 string
var v12 string
var v13 bool
var v14 bool
var v15 string
var v16 string
var v17 int64
var v18 int64
var v19 []byte
var v15 bool
var v16 []byte
v0 = v.UserID
v1 = v.Owner
v2 = v.Name
v3 = v.FullName
v4 = v.Token
v5 = v.Language
v6 = v.Private
v7 = v.Self
v8 = v.Link
v9 = v.Clone
v10 = v.Branch
v11 = v.Timeout
v12 = v.Trusted
v13 = v.PostCommit
v14 = v.PullRequest
v15 = v.PublicKey
v16 = v.PrivateKey
v17 = v.Created
v18 = v.Updated
v19, _ = json.Marshal(v.Params)
v4 = v.Self
v5 = v.Link
v6 = v.Clone
v7 = v.Branch
v8 = v.Private
v9 = v.Trusted
v10 = v.Timeout
if v.Keys != nil {
v11 = v.Keys.Public
v12 = v.Keys.Private
}
if v.Hooks != nil {
v13 = v.Hooks.PullRequest
v14 = v.Hooks.Push
v15 = v.Hooks.Tags
}
v16, _ = json.Marshal(v.Params)
res, err := db.Exec(query,
&v0,
@ -93,9 +91,6 @@ func createRepo(db repoDB, query string, v *Repo) error {
&v14,
&v15,
&v16,
&v17,
&v18,
&v19,
)
if err != nil {
return err
@ -113,41 +108,39 @@ func updateRepo(db repoDB, query string, v *Repo) error {
var v4 string
var v5 string
var v6 string
var v7 bool
var v7 string
var v8 string
var v9 string
var v10 string
var v11 string
var v12 int64
var v13 bool
var v9 bool
var v10 bool
var v11 int64
var v12 string
var v13 string
var v14 bool
var v15 bool
var v16 string
var v17 string
var v18 int64
var v19 int64
var v20 []byte
var v16 bool
var v17 []byte
v0 = v.ID
v1 = v.UserID
v2 = v.Owner
v3 = v.Name
v4 = v.FullName
v5 = v.Token
v6 = v.Language
v7 = v.Private
v8 = v.Self
v9 = v.Link
v10 = v.Clone
v11 = v.Branch
v12 = v.Timeout
v13 = v.Trusted
v14 = v.PostCommit
v15 = v.PullRequest
v16 = v.PublicKey
v17 = v.PrivateKey
v18 = v.Created
v19 = v.Updated
v20, _ = json.Marshal(v.Params)
v5 = v.Self
v6 = v.Link
v7 = v.Clone
v8 = v.Branch
v9 = v.Private
v10 = v.Trusted
v11 = v.Timeout
if v.Keys != nil {
v12 = v.Keys.Public
v13 = v.Keys.Private
}
if v.Hooks != nil {
v14 = v.Hooks.PullRequest
v15 = v.Hooks.Push
v16 = v.Hooks.Tags
}
v17, _ = json.Marshal(v.Params)
_, err := db.Exec(query,
&v1,
@ -167,9 +160,6 @@ func updateRepo(db repoDB, query string, v *Repo) error {
&v15,
&v16,
&v17,
&v18,
&v19,
&v20,
&v0,
)
return err
@ -183,20 +173,17 @@ func scanRepo(row *sql.Row) (*Repo, error) {
var v4 string
var v5 string
var v6 string
var v7 bool
var v7 string
var v8 string
var v9 string
var v10 string
var v11 string
var v12 int64
var v13 bool
var v9 bool
var v10 bool
var v11 int64
var v12 string
var v13 string
var v14 bool
var v15 bool
var v16 string
var v17 string
var v18 int64
var v19 int64
var v20 []byte
var v16 bool
var v17 []byte
err := row.Scan(
&v0,
@ -217,9 +204,6 @@ func scanRepo(row *sql.Row) (*Repo, error) {
&v15,
&v16,
&v17,
&v18,
&v19,
&v20,
)
if err != nil {
return nil, err
@ -231,22 +215,21 @@ func scanRepo(row *sql.Row) (*Repo, error) {
v.Owner = v2
v.Name = v3
v.FullName = v4
v.Token = v5
v.Language = v6
v.Private = v7
v.Self = v8
v.Link = v9
v.Clone = v10
v.Branch = v11
v.Timeout = v12
v.Trusted = v13
v.PostCommit = v14
v.PullRequest = v15
v.PublicKey = v16
v.PrivateKey = v17
v.Created = v18
v.Updated = v19
json.Unmarshal(v20, &v.Params)
v.Self = v5
v.Link = v6
v.Clone = v7
v.Branch = v8
v.Private = v9
v.Trusted = v10
v.Timeout = v11
v.Keys = &Keypair{}
v.Keys.Public = v12
v.Keys.Private = v13
v.Hooks = &Hooks{}
v.Hooks.PullRequest = v14
v.Hooks.Push = v15
v.Hooks.Tags = v16
json.Unmarshal(v17, &v.Params)
return v, nil
}
@ -262,20 +245,17 @@ func scanRepos(rows *sql.Rows) ([]*Repo, error) {
var v4 string
var v5 string
var v6 string
var v7 bool
var v7 string
var v8 string
var v9 string
var v10 string
var v11 string
var v12 int64
var v13 bool
var v9 bool
var v10 bool
var v11 int64
var v12 string
var v13 string
var v14 bool
var v15 bool
var v16 string
var v17 string
var v18 int64
var v19 int64
var v20 []byte
var v16 bool
var v17 []byte
err = rows.Scan(
&v0,
&v1,
@ -295,9 +275,6 @@ func scanRepos(rows *sql.Rows) ([]*Repo, error) {
&v15,
&v16,
&v17,
&v18,
&v19,
&v20,
)
if err != nil {
return vv, err
@ -309,22 +286,21 @@ func scanRepos(rows *sql.Rows) ([]*Repo, error) {
v.Owner = v2
v.Name = v3
v.FullName = v4
v.Token = v5
v.Language = v6
v.Private = v7
v.Self = v8
v.Link = v9
v.Clone = v10
v.Branch = v11
v.Timeout = v12
v.Trusted = v13
v.PostCommit = v14
v.PullRequest = v15
v.PublicKey = v16
v.PrivateKey = v17
v.Created = v18
v.Updated = v19
json.Unmarshal(v20, &v.Params)
v.Self = v5
v.Link = v6
v.Clone = v7
v.Branch = v8
v.Private = v9
v.Trusted = v10
v.Timeout = v11
v.Keys = &Keypair{}
v.Keys.Public = v12
v.Keys.Private = v13
v.Hooks = &Hooks{}
v.Hooks.PullRequest = v14
v.Hooks.Push = v15
v.Hooks.Tags = v16
json.Unmarshal(v17, &v.Params)
vv = append(vv, v)
}
return vv, rows.Err()
@ -337,21 +313,18 @@ SELECT
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
`
@ -363,21 +336,18 @@ SELECT
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
LIMIT ? OFFSET ?
@ -390,26 +360,47 @@ SELECT
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
WHERE repo_id = ?
`
const stmtRepoSelectRepoUserId = `
SELECT
repo_id
,repo_user_id
,repo_owner
,repo_name
,repo_full_name
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_private
,repo_trusted
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
WHERE repo_user_id = ?
`
const stmtRepoSelectRepoOwnerName = `
SELECT
repo_id
@ -417,21 +408,18 @@ SELECT
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
WHERE repo_owner = ?
@ -445,49 +433,48 @@ SELECT
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
FROM repos
WHERE repo_full_name = ?
`
const stmtRepoSelectCount = `
SELECT count(1)
FROM repos
`
const stmtRepoInsert = `
INSERT INTO repos (
repo_user_id
,repo_owner
,repo_name
,repo_full_name
,repo_token
,repo_language
,repo_private
,repo_self
,repo_link
,repo_clone
,repo_branch
,repo_timeout
,repo_private
,repo_trusted
,repo_post_commit
,repo_pull_request
,repo_public_key
,repo_private_key
,repo_created
,repo_updated
,repo_timeout
,repo_keys_public
,repo_keys_private
,repo_hooks_pull_request
,repo_hooks_push
,repo_hooks_tags
,repo_params
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
`
const stmtRepoUpdate = `
@ -496,21 +483,18 @@ UPDATE repos SET
,repo_owner = ?
,repo_name = ?
,repo_full_name = ?
,repo_token = ?
,repo_language = ?
,repo_private = ?
,repo_self = ?
,repo_link = ?
,repo_clone = ?
,repo_branch = ?
,repo_timeout = ?
,repo_private = ?
,repo_trusted = ?
,repo_post_commit = ?
,repo_pull_request = ?
,repo_public_key = ?
,repo_private_key = ?
,repo_created = ?
,repo_updated = ?
,repo_timeout = ?
,repo_keys_public = ?
,repo_keys_private = ?
,repo_hooks_pull_request = ?
,repo_hooks_push = ?
,repo_hooks_tags = ?
,repo_params = ?
WHERE repo_id = ?
`
@ -527,25 +511,26 @@ CREATE TABLE IF NOT EXISTS repos (
,repo_owner VARCHAR
,repo_name VARCHAR
,repo_full_name VARCHAR
,repo_token VARCHAR
,repo_language VARCHAR
,repo_private BOOLEAN
,repo_self VARCHAR
,repo_link VARCHAR
,repo_clone VARCHAR
,repo_branch VARCHAR
,repo_timeout INTEGER
,repo_private BOOLEAN
,repo_trusted BOOLEAN
,repo_post_commit BOOLEAN
,repo_pull_request BOOLEAN
,repo_public_key VARCHAR
,repo_private_key VARCHAR
,repo_created INTEGER
,repo_updated INTEGER
,repo_timeout INTEGER
,repo_keys_public VARCHAR
,repo_keys_private VARCHAR
,repo_hooks_pull_requestBOOLEAN
,repo_hooks_push BOOLEAN
,repo_hooks_tags BOOLEAN
,repo_params BLOB
);
`
const stmtRepoRepoUserIdIndex = `
CREATE INDEX IF NOT EXISTS ix_repo_user_id ON repos (repo_user_id);
`
const stmtRepoRepoOwnerNameIndex = `
CREATE UNIQUE INDEX IF NOT EXISTS ux_repo_owner_name ON repos (repo_owner,repo_name);
`

View file

@ -16,11 +16,3 @@ type Netrc struct {
Login string `json:"login"`
Password string `json:"user"`
}
// Keypair represents an RSA public and private key
// assigned to a repository. It may be used to clone
// private repositories, or as a deployment key.
type Keypair struct {
Public string `json:"public,omitempty"`
Private string `json:"private,omitempty"`
}

View file

@ -6,23 +6,26 @@ type Repo struct {
Owner string `meddler:"repo_owner" json:"owner" sql:"unique:ux_repo_owner_name"`
Name string `meddler:"repo_name" json:"name" sql:"unique:ux_repo_owner_name"`
FullName string `meddler:"repo_full_name" json:"full_name" sql:"unique:ux_repo_full_name"`
Token string `meddler:"repo_token" json:"-"`
Language string `meddler:"repo_language" json:"language"`
Private bool `meddler:"repo_private" json:"private"`
Self string `meddler:"repo_self" json:"self_url"`
Link string `meddler:"repo_link" json:"link_url"`
Clone string `meddler:"repo_clone" json:"clone_url"`
Branch string `meddler:"repo_branch" json:"default_branch"`
Timeout int64 `meddler:"repo_timeout" json:"timeout"`
Private bool `meddler:"repo_private" json:"private"`
Trusted bool `meddler:"repo_trusted" json:"trusted"`
PostCommit bool `meddler:"repo_post_commit" json:"post_commits"`
PullRequest bool `meddler:"repo_pull_request" json:"pull_requests"`
PublicKey string `meddler:"repo_public_key" json:"-"`
PrivateKey string `meddler:"repo_private_key" json:"-"`
Created int64 `meddler:"repo_created" json:"created_at"`
Updated int64 `meddler:"repo_updated" json:"updated_at"`
Timeout int64 `meddler:"repo_timeout" json:"timeout"`
Params map[string]string `meddler:"repo_params,json" json:"-"`
Keys *Keypair `json:"-"`
Hooks *Hooks `json:"hooks"`
// Perms are the current user's permissions to push,
// pull, and administer this repository. The permissions
// are sourced from the version control system (ie GitHub)
Perms *Perm `json:"perms,omitempty" sql:"-"`
// Params are private environment parameters that are
// considered secret and are therefore stored external
// to the source code repository inside Drone.
Params map[string]string `json:"-"`
}
type RepoLite struct {
@ -49,7 +52,21 @@ type RepoCommit struct {
}
type Perm struct {
Pull bool `json:"pull"`
Push bool `json:"push"`
Admin bool `json:"admin"`
Pull bool `json:"pull" sql:"-"`
Push bool `json:"push" sql:"-"`
Admin bool `json:"admin" sql:"-"`
}
type Hooks struct {
PullRequest bool `json:"pull_request"`
Push bool `json:"push"`
Tags bool `json:"tags"`
}
// Keypair represents an RSA public and private key
// assigned to a repository. It may be used to clone
// private repositories, or as a deployment key.
type Keypair struct {
Public string `json:"public,omitempty"`
Private string `json:"private,omitempty"`
}