@@ -150,25 +150,55 @@ public void copyAttachments(AttachmentCopyEventContext context) throws IOExcepti
150150
151151 List <Map <String , String >> attachmentsMetadata = copyResult .getAttachmentsMetadata ();
152152 List <CmisDocument > populatedDocuments = copyResult .getPopulatedDocuments ();
153+ List <Map <String , String >> copyFailures = copyResult .getFailedAttachments ();
153154
154- String upIdKey = resolveUpIdKey (context , parentEntity , compositionName );
155+ // Show warning if there are failures
156+ if (!copyFailures .isEmpty ()) {
157+ StringBuilder warningMessage =
158+ new StringBuilder ("Failed to copy the following attachments:\n " );
159+ for (Map <String , String > failure : copyFailures ) {
160+ warningMessage
161+ .append (" - ObjectId: " )
162+ .append (failure .get (OBJECT_ID_KEY ))
163+ .append (", Reason: " )
164+ .append (failure .get (FAILURE_REASON_KEY ))
165+ .append ("\n " );
166+ }
167+ context .getMessages ().warn (warningMessage .toString ());
168+ }
155169
156- CreateDraftEntriesRequest draftRequest =
157- CreateDraftEntriesRequest .builder ()
158- .attachmentsMetadata (attachmentsMetadata )
159- .populatedDocuments (populatedDocuments )
160- .parentEntity (parentEntity )
161- .compositionName (compositionName )
162- .upID (upID )
163- .upIdKey (upIdKey )
164- .repositoryId (repositoryId )
165- .folderId (folderId )
166- .customPropertyValues (null )
167- .build ();
170+ String upIdKey = resolveUpIdKey (context , parentEntity , compositionName );
168171
169- // Pass the entity for type conversion
170- CdsEntity targetEntity = entity .isPresent () ? entity .get () : null ;
171- createDraftEntries (draftRequest , customPropertyDefinitions , targetEntity );
172+ // Create draft entries if there are successful copies
173+ if (!attachmentsMetadata .isEmpty ()) {
174+ CreateDraftEntriesRequest draftRequest =
175+ CreateDraftEntriesRequest .builder ()
176+ .attachmentsMetadata (attachmentsMetadata )
177+ .populatedDocuments (populatedDocuments )
178+ .parentEntity (parentEntity )
179+ .compositionName (compositionName )
180+ .upID (upID )
181+ .upIdKey (upIdKey )
182+ .repositoryId (repositoryId )
183+ .folderId (folderId )
184+ .customPropertyValues (null )
185+ .build ();
186+
187+ // Pass the entity for type conversion
188+ CdsEntity targetEntity = entity .isPresent () ? entity .get () : null ;
189+ createDraftEntries (draftRequest , customPropertyDefinitions , targetEntity );
190+ } else if (!folderExists ) {
191+ // All copies failed but folder was newly created - delete the empty folder
192+ logger .info (
193+ "All attachments failed to copy. Deleting newly created empty folder: {}" , folderId );
194+ try {
195+ sdmService .deleteDocument (
196+ "deleteTree" , folderId , context .getUserInfo ().getName (), isSystemUser );
197+ logger .info ("Deleted empty folder: {}" , folderId );
198+ } catch (Exception e ) {
199+ logger .error ("Failed to delete empty folder {}: {}" , folderId , e .getMessage ());
200+ }
201+ }
172202
173203 logger .info (
174204 "Copy attachments completed - {} attachments copied for upID: {}" ,
@@ -570,6 +600,33 @@ private CopyAttachmentsResult copyAttachmentsToSDM(
570600 logger .debug ("START: Copy {} attachments to SDM" , request .getObjectIds ().size ());
571601 List <Map <String , String >> attachmentsMetadata = new ArrayList <>();
572602 List <CmisDocument > populatedDocuments = new ArrayList <>();
603+ List <Map <String , String >> failedAttachments = new ArrayList <>();
604+
605+ // Fetch valid secondary properties for validation
606+ List <String > validSecondaryProperties = new ArrayList <>();
607+ boolean shouldValidateSecondaryProperties = false ;
608+ if (!customPropertiesInSDM .isEmpty ()) {
609+ try {
610+ List <String > secondaryTypes =
611+ sdmService .getSecondaryTypes (
612+ request .getRepositoryId (), request .getSdmCredentials (), request .getIsSystemUser ());
613+ validSecondaryProperties =
614+ sdmService .getValidSecondaryProperties (
615+ secondaryTypes ,
616+ request .getSdmCredentials (),
617+ request .getRepositoryId (),
618+ request .getIsSystemUser ());
619+ shouldValidateSecondaryProperties = true ;
620+ logger .debug (
621+ "Fetched {} valid secondary properties for copy validation" ,
622+ validSecondaryProperties .size ());
623+ } catch (Exception e ) {
624+ logger .warn (
625+ "Failed to fetch valid secondary properties for copy validation: {}. "
626+ + "Proceeding without validation." ,
627+ e .getMessage ());
628+ }
629+ }
573630
574631 for (String objectId : request .getObjectIds ()) {
575632 logger .debug ("Processing copy for objectId: {}" , objectId );
@@ -584,7 +641,6 @@ private CopyAttachmentsResult copyAttachmentsToSDM(
584641 populatedDocument .setType (cmisDocument .getType ());
585642 populatedDocument .setUrl (cmisDocument .getUrl ());
586643 populatedDocument .setUploadStatus (cmisDocument .getUploadStatus ());
587- populatedDocuments .add (populatedDocument );
588644
589645 try {
590646 Map <String , String > attachmentData =
@@ -594,6 +650,53 @@ private CopyAttachmentsResult copyAttachmentsToSDM(
594650 request .getIsSystemUser (),
595651 customPropertiesInSDM );
596652
653+ // Validate secondary properties after copy (similar to move validation)
654+ // Only validate if we successfully fetched valid secondary properties
655+ List <String > invalidProperties = new ArrayList <>();
656+ if (shouldValidateSecondaryProperties ) {
657+ for (String customProp : customPropertiesInSDM ) {
658+ if (attachmentData .containsKey (customProp )
659+ && !validSecondaryProperties .contains (customProp )) {
660+ invalidProperties .add (customProp );
661+ }
662+ }
663+ }
664+
665+ if (!invalidProperties .isEmpty ()) {
666+ // Validation failed - delete the copied attachment and record failure
667+ String copiedObjectId = attachmentData .get ("cmis:objectId" );
668+ logger .error (
669+ "Attachment {} validation FAILED - Found {} invalid properties: {}. "
670+ + "Deleting copied attachment..." ,
671+ objectId ,
672+ invalidProperties .size (),
673+ invalidProperties );
674+ try {
675+ sdmService .deleteDocument (
676+ "delete" ,
677+ copiedObjectId ,
678+ request .getContext ().getUserInfo ().getName (),
679+ request .getIsSystemUser ());
680+ logger .info ("Deleted invalid copied attachment: {}" , copiedObjectId );
681+ } catch (Exception deleteEx ) {
682+ logger .error (
683+ "Failed to delete invalid copied attachment {}: {}" ,
684+ copiedObjectId ,
685+ deleteEx .getMessage ());
686+ }
687+ // Add to failed attachments list instead of throwing exception
688+ Map <String , String > failure = new HashMap <>();
689+ failure .put (OBJECT_ID_KEY , objectId );
690+ failure .put (
691+ FAILURE_REASON_KEY ,
692+ SDMUtils .getErrorMessage ("INVALID_SECONDARY_PROPERTIES_FOR_COPY_PREFIX" )
693+ + String .join (", " , invalidProperties )
694+ + SDMUtils .getErrorMessage ("INVALID_SECONDARY_PROPERTIES_FOR_COPY_SUFFIX" ));
695+ failedAttachments .add (failure );
696+ continue ;
697+ }
698+
699+ populatedDocuments .add (populatedDocument );
597700 attachmentsMetadata .add (attachmentData );
598701 logger .debug ("Successfully copied attachment: {}" , objectId );
599702 } catch (ServiceException e ) {
@@ -607,8 +710,11 @@ private CopyAttachmentsResult copyAttachmentsToSDM(
607710 }
608711 }
609712
610- logger .debug ("END: Copy attachments to SDM - {} successful" , attachmentsMetadata .size ());
611- return new CopyAttachmentsResult (attachmentsMetadata , populatedDocuments );
713+ logger .debug (
714+ "END: Copy attachments to SDM - {} successful, {} failed" ,
715+ attachmentsMetadata .size (),
716+ failedAttachments .size ());
717+ return new CopyAttachmentsResult (attachmentsMetadata , populatedDocuments , failedAttachments );
612718 }
613719
614720 /**
0 commit comments