github.com/cobalt77/jfrog-client-go@v0.14.5/artifactory/services/movecopy.go (about) 1 package services 2 3 import ( 4 "errors" 5 "net/http" 6 "path" 7 "strconv" 8 "strings" 9 10 rthttpclient "github.com/cobalt77/jfrog-client-go/artifactory/httpclient" 11 "github.com/cobalt77/jfrog-client-go/artifactory/services/utils" 12 "github.com/cobalt77/jfrog-client-go/auth" 13 clientutils "github.com/cobalt77/jfrog-client-go/utils" 14 "github.com/cobalt77/jfrog-client-go/utils/errorutils" 15 "github.com/cobalt77/jfrog-client-go/utils/io/content" 16 "github.com/cobalt77/jfrog-client-go/utils/io/fileutils" 17 "github.com/cobalt77/jfrog-client-go/utils/log" 18 ) 19 20 const ( 21 MOVE MoveType = "move" 22 COPY MoveType = "copy" 23 ) 24 25 type MoveCopyService struct { 26 moveType MoveType 27 client *rthttpclient.ArtifactoryHttpClient 28 DryRun bool 29 ArtDetails auth.ServiceDetails 30 } 31 32 func NewMoveCopyService(client *rthttpclient.ArtifactoryHttpClient, moveType MoveType) *MoveCopyService { 33 return &MoveCopyService{moveType: moveType, client: client} 34 } 35 36 func (mc *MoveCopyService) GetArtifactoryDetails() auth.ServiceDetails { 37 return mc.ArtDetails 38 } 39 40 func (mc *MoveCopyService) SetArtifactoryDetails(rt auth.ServiceDetails) { 41 mc.ArtDetails = rt 42 } 43 44 func (mc *MoveCopyService) IsDryRun() bool { 45 return mc.DryRun 46 } 47 48 func (mc *MoveCopyService) GetJfrogHttpClient() (*rthttpclient.ArtifactoryHttpClient, error) { 49 return mc.client, nil 50 } 51 52 func (mc *MoveCopyService) MoveCopyServiceMoveFilesWrapper(moveSpec MoveCopyParams) (successCount, failedCount int, err error) { 53 var resultReader *content.ContentReader 54 log.Info("Searching items...") 55 56 switch moveSpec.GetSpecType() { 57 case utils.BUILD: 58 resultReader, err = utils.SearchBySpecWithBuild(moveSpec.GetFile(), mc) 59 case utils.AQL: 60 resultReader, err = utils.SearchBySpecWithAql(moveSpec.GetFile(), mc, utils.NONE) 61 case utils.WILDCARD: 62 moveSpec.SetIncludeDir(true) 63 resultReader, err = utils.SearchBySpecWithPattern(moveSpec.GetFile(), mc, utils.NONE) 64 } 65 if err != nil { 66 return 0, 0, err 67 } 68 defer resultReader.Close() 69 successCount, failedCount, err = mc.moveFiles(resultReader, moveSpec) 70 if err != nil { 71 return 0, 0, err 72 } 73 74 log.Debug(moveMsgs[mc.moveType].MovedMsg, strconv.Itoa(successCount), "artifacts.") 75 if failedCount > 0 { 76 err = errorutils.CheckError(errors.New("Failed " + moveMsgs[mc.moveType].MovingMsg + " " + strconv.Itoa(failedCount) + " artifacts.")) 77 } 78 return 79 } 80 81 func reduceMovePaths(cr *content.ContentReader, params MoveCopyParams) (*content.ContentReader, error) { 82 if params.IsFlat() { 83 return utils.ReduceBottomChainDirResult(cr) 84 } 85 return utils.ReduceTopChainDirResult(cr) 86 } 87 88 func (mc *MoveCopyService) moveFiles(reader *content.ContentReader, params MoveCopyParams) (successCount, failedCount int, err error) { 89 successCount, failedCount = 0, 0 90 reduceMovePathsReader, err := reduceMovePaths(reader, params) 91 if err != nil { 92 return 93 } 94 defer reduceMovePathsReader.Close() 95 length, err := reduceMovePathsReader.Length() 96 if err != nil { 97 return 98 } 99 utils.LogSearchResults(length) 100 for resultItem := new(utils.ResultItem); reduceMovePathsReader.NextRecord(resultItem) == nil; resultItem = new(utils.ResultItem) { 101 destPathLocal := params.GetFile().Target 102 if !params.IsFlat() { 103 if strings.Contains(destPathLocal, "/") { 104 file, dir := fileutils.GetFileAndDirFromPath(destPathLocal) 105 destPathLocal = clientutils.TrimPath(dir + "/" + resultItem.Path + "/" + file) 106 } else { 107 destPathLocal = clientutils.TrimPath(destPathLocal + "/" + resultItem.Path + "/") 108 } 109 } 110 destFile, err := clientutils.BuildTargetPath(params.GetFile().Pattern, resultItem.GetItemRelativePath(), destPathLocal, true) 111 if err != nil { 112 log.Error(err) 113 continue 114 } 115 if strings.HasSuffix(destFile, "/") { 116 if resultItem.Type != "folder" { 117 destFile += resultItem.Name 118 } else { 119 mc.createPathForMoveAction(destFile) 120 } 121 } 122 success, err := mc.moveFile(resultItem.GetItemRelativePath(), destFile) 123 if err != nil { 124 log.Error(err) 125 continue 126 } 127 128 successCount += clientutils.Bool2Int(success) 129 failedCount += clientutils.Bool2Int(!success) 130 } 131 return 132 } 133 134 func (mc *MoveCopyService) moveFile(sourcePath, destPath string) (bool, error) { 135 message := moveMsgs[mc.moveType].MovingMsg + " artifact: " + sourcePath + " to: " + destPath 136 moveUrl := mc.GetArtifactoryDetails().GetUrl() 137 restApi := path.Join("api", string(mc.moveType), sourcePath) 138 params := map[string]string{"to": destPath} 139 if mc.IsDryRun() { 140 log.Info("[Dry run]", message) 141 params["dry"] = "1" 142 } else { 143 log.Info(message) 144 } 145 requestFullUrl, err := utils.BuildArtifactoryUrl(moveUrl, restApi, params) 146 if err != nil { 147 return false, err 148 } 149 httpClientsDetails := mc.GetArtifactoryDetails().CreateHttpClientDetails() 150 151 resp, body, err := mc.client.SendPost(requestFullUrl, nil, &httpClientsDetails) 152 if err != nil { 153 return false, err 154 } 155 156 if resp.StatusCode != http.StatusOK { 157 log.Error("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)) 158 } 159 160 log.Debug("Artifactory response:", resp.Status) 161 return resp.StatusCode == http.StatusOK, nil 162 } 163 164 // Create destPath in Artifactory 165 func (mc *MoveCopyService) createPathForMoveAction(destPath string) (bool, error) { 166 if mc.IsDryRun() == true { 167 log.Info("[Dry run]", "Create path:", destPath) 168 return true, nil 169 } 170 171 return mc.createPathInArtifactory(destPath, mc) 172 } 173 174 func (mc *MoveCopyService) createPathInArtifactory(destPath string, conf utils.CommonConf) (bool, error) { 175 rtUrl := conf.GetArtifactoryDetails().GetUrl() 176 requestFullUrl, err := utils.BuildArtifactoryUrl(rtUrl, destPath, map[string]string{}) 177 if err != nil { 178 return false, err 179 } 180 httpClientsDetails := conf.GetArtifactoryDetails().CreateHttpClientDetails() 181 resp, body, err := mc.client.SendPut(requestFullUrl, nil, &httpClientsDetails) 182 if err != nil { 183 return false, err 184 } 185 186 if resp.StatusCode != http.StatusCreated { 187 log.Error("Artifactory response: " + resp.Status + "\n" + clientutils.IndentJson(body)) 188 } 189 190 log.Debug("Artifactory response:", resp.Status) 191 return resp.StatusCode == http.StatusOK, nil 192 } 193 194 var moveMsgs = map[MoveType]MoveOptions{ 195 MOVE: {MovingMsg: "Moving", MovedMsg: "Moved"}, 196 COPY: {MovingMsg: "Copying", MovedMsg: "Copied"}, 197 } 198 199 type MoveOptions struct { 200 MovingMsg string 201 MovedMsg string 202 } 203 204 type MoveType string 205 206 type MoveCopyParams struct { 207 *utils.ArtifactoryCommonParams 208 Flat bool 209 } 210 211 func (mc *MoveCopyParams) GetFile() *utils.ArtifactoryCommonParams { 212 return mc.ArtifactoryCommonParams 213 } 214 215 func (mc *MoveCopyParams) SetIncludeDir(isIncludeDir bool) { 216 mc.GetFile().IncludeDirs = isIncludeDir 217 } 218 219 func (mc *MoveCopyParams) IsFlat() bool { 220 return mc.Flat 221 } 222 223 func NewMoveCopyParams() MoveCopyParams { 224 return MoveCopyParams{ArtifactoryCommonParams: &utils.ArtifactoryCommonParams{}} 225 }