diff options
author | andre.schmid <andre.schmid@est.tech> | 2022-03-25 19:30:14 +0000 |
---|---|---|
committer | andre.schmid <andre.schmid@est.tech> | 2022-03-31 18:19:04 +0100 |
commit | 115b2ef065fa652b380093e4f2571e53c3f0bc19 (patch) | |
tree | c325e09c003f2d8d55b09692ffbd4e79e53da3aa /openecomp-be/api/openecomp-sdc-rest-webapp | |
parent | 6cffd9e3eecb10281d880ea7d217f0ecddf8fb5a (diff) |
Fix package storage and reducer config reload
The package storage configuration was not being reloaded when a
configuration file change was made.
Increases OrchestrationTemplateCandidateImpl coverage by testing the
upload to the artifact storage.
Issue-ID: SDC-3934
Change-Id: I533b3c3a92cdadb60a375890da85ee053364e8af
Signed-off-by: andre.schmid <andre.schmid@est.tech>
Diffstat (limited to 'openecomp-be/api/openecomp-sdc-rest-webapp')
4 files changed, 123 insertions, 74 deletions
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java index 93483f3de1..ec1e96e0bd 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java @@ -29,6 +29,7 @@ import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_ import static org.openecomp.sdc.common.errors.Messages.ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE; import static org.openecomp.sdc.common.errors.Messages.NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST; import static org.openecomp.sdc.common.errors.Messages.PACKAGE_PROCESS_ERROR; +import static org.openecomp.sdc.common.errors.Messages.PACKAGE_REDUCER_NOT_CONFIGURED; import static org.openecomp.sdc.common.errors.Messages.UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING; import static org.openecomp.sdcrests.vsp.rest.exception.OrchestrationTemplateCandidateUploadManagerExceptionSupplier.vspUploadAlreadyInProgress; @@ -103,21 +104,15 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate private final OrchestrationTemplateCandidateManager candidateManager; private final VendorSoftwareProductManager vendorSoftwareProductManager; private final ActivityLogManager activityLogManager; - private final ArtifactStorageManager artifactStorageManager; - private final StorageFactory storageFactory; - private final PackageSizeReducer packageSizeReducer; private final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager; + private final StorageFactory storageFactory; @Autowired public OrchestrationTemplateCandidateImpl(final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager) { this.candidateManager = OrchestrationTemplateCandidateManagerFactory.getInstance().createInterface(); this.vendorSoftwareProductManager = VspManagerFactory.getInstance().createInterface(); this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface(); - LOGGER.info("Instantiating artifactStorageManager"); this.storageFactory = new StorageFactory(); - this.artifactStorageManager = storageFactory.createArtifactStorageManager(); - LOGGER.info("Instantiating packageSizeReducer"); - this.packageSizeReducer = storageFactory.createPackageSizeReducer().orElse(null); this.orchestrationTemplateCandidateUploadManager = orchestrationTemplateCandidateUploadManager; } @@ -125,15 +120,12 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate public OrchestrationTemplateCandidateImpl(final OrchestrationTemplateCandidateManager candidateManager, final VendorSoftwareProductManager vendorSoftwareProductManager, final ActivityLogManager activityLogManager, - final ArtifactStorageManager artifactStorageManager, - final PackageSizeReducer packageSizeReducer, - final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager) { + final OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager, + final StorageFactory storageFactory) { this.candidateManager = candidateManager; this.vendorSoftwareProductManager = vendorSoftwareProductManager; this.activityLogManager = activityLogManager; - this.artifactStorageManager = artifactStorageManager; - this.storageFactory = new StorageFactory(); - this.packageSizeReducer = packageSizeReducer; + this.storageFactory = storageFactory; this.orchestrationTemplateCandidateUploadManager = orchestrationTemplateCandidateUploadManager; } @@ -156,7 +148,7 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate ArtifactInfo artifactInfo = null; final ArtifactStorageManager artifactStorageManager = storageFactory.createArtifactStorageManager(); if (artifactStorageManager.isEnabled()) { - artifactInfo = handleArtifactStorage(vspId, versionId, filename, dataHandler); + artifactInfo = handleArtifactStorage(vspId, versionId, filename, dataHandler, artifactStorageManager); fileToUploadBytes = artifactInfo.getBytes(); } else { fileToUploadBytes = fileToUpload.getObject(byte[].class); @@ -215,7 +207,13 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate } private ArtifactInfo handleArtifactStorage(final String vspId, final String versionId, final String filename, - final DataHandler artifactDataHandler) { + final DataHandler artifactDataHandler, + final ArtifactStorageManager artifactStorageManager) { + final PackageSizeReducer packageSizeReducer = storageFactory.createPackageSizeReducer().orElse(null); + if (packageSizeReducer == null) { + throw new ArtifactStorageException(PACKAGE_REDUCER_NOT_CONFIGURED.getErrorMessage()); + } + final Path tempArtifactPath; try { final ArtifactStorageConfig storageConfiguration = artifactStorageManager.getStorageConfiguration(); @@ -239,15 +237,22 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate LOGGER.error("Package Size Reducer not configured", e); throw new ArtifactStorageException(ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)); } + try { - LOGGER.debug("STARTED -> reducing '{}'", tempArtifactPath.toString()); + LOGGER.debug("STARTED -> reducing '{}'", tempArtifactPath); artifactInfo.setBytes(packageSizeReducer.reduce(tempArtifactPath)); - LOGGER.debug("FINISHED -> reducing '{}'", tempArtifactPath.toString()); + LOGGER.debug("FINISHED -> reducing '{}'", tempArtifactPath); + } catch (final Exception e) { + LOGGER.debug("ERROR -> reducing '{}'", tempArtifactPath, e); + throw new ArtifactStorageException(ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(filename), e); + } + + try { Files.delete(tempArtifactPath); } catch (final Exception e) { - LOGGER.error("Package Size Reducer not configured", e); - throw new ArtifactStorageException(ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(filename)); + LOGGER.warn("Could not delete temporary package at '{}'", tempArtifactPath, e); } + return artifactInfo; } diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImpl.java index 5540e0f315..9bb68f23af 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImpl.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImpl.java @@ -133,10 +133,9 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { private final ActivityLogManager activityLogManager; private final NotificationPropagationManager notifier; private final UniqueValueUtil uniqueValueUtil; - private final ArtifactStorageManager artifactStorageManager; + private final StorageFactory storageFactory; private final CatalogVspClient catalogVspClient; - public VendorSoftwareProductsImpl() { this.itemManager = AsdcItemManagerFactory.getInstance().createInterface(); this.permissionsManager = PermissionsManagerFactory.getInstance().createInterface(); @@ -145,7 +144,7 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { this.activityLogManager = ActivityLogManagerFactory.getInstance().createInterface(); this.notifier = NotificationPropagationManagerFactory.getInstance().createInterface(); this.uniqueValueUtil = new UniqueValueUtil(UniqueValueDaoFactory.getInstance().createInterface()); - this.artifactStorageManager = new StorageFactory().createArtifactStorageManager(); + this.storageFactory = new StorageFactory(); this.catalogVspClient = new CatalogVspClientImpl(); } @@ -156,7 +155,7 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { ActivityLogManager activityLogManager, NotificationPropagationManager notifier, UniqueValueUtil uniqueValueUtil, - ArtifactStorageManager artifactStorageManager, + final StorageFactory storageFactory, CatalogVspClient catalogVspClient) { this.itemManager = itemManager; this.permissionsManager = permissionsManager; @@ -165,7 +164,7 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { this.activityLogManager = activityLogManager; this.notifier = notifier; this.uniqueValueUtil = uniqueValueUtil; - this.artifactStorageManager = artifactStorageManager; + this.storageFactory = storageFactory; this.catalogVspClient = catalogVspClient; } @@ -299,8 +298,9 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { } Integer certifiedVersionsCounter = vsp.getVersionStatusCounters().get(VersionStatus.Certified); + final ArtifactStorageManager artifactStorageManager = storageFactory.createArtifactStorageManager(); if (Objects.isNull(certifiedVersionsCounter) || certifiedVersionsCounter == 0) { - if (artifactStorageManager.isEnabled() && !deleteVspFromStorage(vspId)) { + if (artifactStorageManager.isEnabled() && !deleteVspFromStorage(vspId, artifactStorageManager)) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(new Exception(Messages.DELETE_VSP_FROM_STORAGE_ERROR.formatMessage(vspId))).build(); } @@ -308,7 +308,7 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { } else { final var isVspArchived = getVspList(null, ItemStatus.ARCHIVED.name(), user).stream().anyMatch(item -> item.getId().equals(vspId)); if (isVspArchived) { - if (artifactStorageManager.isEnabled() && !deleteVspFromStorage(vspId)) { + if (artifactStorageManager.isEnabled() && !deleteVspFromStorage(vspId, artifactStorageManager)) { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(new Exception(Messages.DELETE_VSP_FROM_STORAGE_ERROR.formatMessage(vspId))).build(); } @@ -318,7 +318,7 @@ public class VendorSoftwareProductsImpl implements VendorSoftwareProducts { } } - private boolean deleteVspFromStorage(final String vspId) { + private boolean deleteVspFromStorage(final String vspId, final ArtifactStorageManager artifactStorageManager) { try { artifactStorageManager.delete(vspId); } catch (final Exception e) { diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java index 8b31261d6a..8fd160a886 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java @@ -54,6 +54,8 @@ import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -99,8 +101,6 @@ class OrchestrationTemplateCandidateImplTest { @Mock private ActivityLogManager activityLogManager; @Mock - private ArtifactStorageManager artifactStorageManager; - @Mock private PackageSizeReducer packageSizeReducer; @Mock private OrchestrationTemplateCandidateUploadManager orchestrationTemplateCandidateUploadManager; @@ -108,54 +108,56 @@ class OrchestrationTemplateCandidateImplTest { private StorageFactory storageFactory; @Mock private Attachment fileToUpload; + @Mock + private ArtifactStorageManager artifactStorageManager; @InjectMocks private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate; + @TempDir + Path tempDir; + @BeforeEach - public void setUp() { - try { - MockitoAnnotations.openMocks(this); - UploadFileResponse uploadFileResponse = new UploadFileResponse(); - uploadFileResponse.setOnboardingType(OnboardingTypesEnum.ZIP); - uploadFileResponse.setNetworkPackageName("test"); - when(candidateManager.upload(any(), any())).thenReturn(uploadFileResponse); - - // get using the candidate manager. - Optional<Pair<String, byte[]>> zipFile = Optional.of(Pair.of("Hello", "World".getBytes())); - - when(candidateManager.get( - ArgumentMatchers.eq(candidateId), - ArgumentMatchers.any())).thenReturn(zipFile); - - when(vendorSoftwareProductManager.get( - ArgumentMatchers.eq(softwareProductId), - ArgumentMatchers.any())).thenReturn(zipFile); - - OrchestrationTemplateActionResponse processResponse = new OrchestrationTemplateActionResponse(); - processResponse.setStatus(UploadFileStatus.Success); - when(candidateManager.process( - ArgumentMatchers.eq(candidateId), - ArgumentMatchers.any())).thenReturn(processResponse); - - ValidationResponse vr = new ValidationResponse(); - when(candidateManager.updateFilesDataStructure( - ArgumentMatchers.eq(candidateId), - ArgumentMatchers.any(), - ArgumentMatchers.any())).thenReturn(vr); - - FilesDataStructure fds = new FilesDataStructure(); - fds.setArtifacts(Arrays.asList("a", "b")); - fds.setNested(Arrays.asList("foo", "bar")); - fds.setUnassigned(Arrays.asList("c", "d")); - fds.setModules(Arrays.asList(new Module(), new Module())); - - when(candidateManager.getFilesDataStructure( - ArgumentMatchers.eq(candidateId), - ArgumentMatchers.any())).thenReturn(Optional.of(fds)); - - } catch (Exception e) { - logger.error(e.getMessage(), e); - } + public void setUp() throws IOException { + MockitoAnnotations.openMocks(this); + UploadFileResponse uploadFileResponse = new UploadFileResponse(); + uploadFileResponse.setOnboardingType(OnboardingTypesEnum.ZIP); + uploadFileResponse.setNetworkPackageName("test"); + when(candidateManager.upload(any(), any())).thenReturn(uploadFileResponse); + + // get using the candidate manager. + Optional<Pair<String, byte[]>> zipFile = Optional.of(Pair.of("Hello", "World".getBytes())); + + when(candidateManager.get( + ArgumentMatchers.eq(candidateId), + ArgumentMatchers.any())).thenReturn(zipFile); + + when(vendorSoftwareProductManager.get( + ArgumentMatchers.eq(softwareProductId), + ArgumentMatchers.any())).thenReturn(zipFile); + + OrchestrationTemplateActionResponse processResponse = new OrchestrationTemplateActionResponse(); + processResponse.setStatus(UploadFileStatus.Success); + when(candidateManager.process( + ArgumentMatchers.eq(candidateId), + ArgumentMatchers.any())).thenReturn(processResponse); + + ValidationResponse vr = new ValidationResponse(); + when(candidateManager.updateFilesDataStructure( + ArgumentMatchers.eq(candidateId), + ArgumentMatchers.any(), + ArgumentMatchers.any())).thenReturn(vr); + + FilesDataStructure fds = new FilesDataStructure(); + fds.setArtifacts(Arrays.asList("a", "b")); + fds.setNested(Arrays.asList("foo", "bar")); + fds.setUnassigned(Arrays.asList("c", "d")); + fds.setModules(Arrays.asList(new Module(), new Module())); + + when(candidateManager.getFilesDataStructure( + ArgumentMatchers.eq(candidateId), + ArgumentMatchers.any())).thenReturn(Optional.of(fds)); + when(storageFactory.createArtifactStorageManager()).thenReturn(artifactStorageManager); + when(storageFactory.createPackageSizeReducer()).thenReturn(Optional.of(packageSizeReducer)); } @Test @@ -194,6 +196,45 @@ class OrchestrationTemplateCandidateImplTest { assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); } + @Test + void uploadArtifactStorageTest() throws IOException { + //given + final String vspId = "vspId"; + final String versionId = "versionId"; + when(orchestrationTemplateCandidateUploadManager.findLatestStatus(vspId, versionId, user)).thenReturn(Optional.empty()); + final UUID lockId = UUID.randomUUID(); + when(orchestrationTemplateCandidateUploadManager.putUploadInProgress(vspId, versionId, user)) + .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.UPLOADING)); + when(orchestrationTemplateCandidateUploadManager.putUploadInValidation(vspId, versionId, user)) + .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.VALIDATING)); + when(orchestrationTemplateCandidateUploadManager.putUploadInProcessing(vspId, versionId, user)) + .thenReturn(createVspUploadStatus(lockId, VspUploadStatus.PROCESSING)); + when(artifactStorageManager.isEnabled()).thenReturn(true); + final MinIoStorageArtifactStorageConfig minIoConfig = + new MinIoStorageArtifactStorageConfig(true, + new EndPoint("", 9000, true), + new Credentials("", ""), tempDir.toString(), 1000 + ); + + when(artifactStorageManager.getStorageConfiguration()).thenReturn(minIoConfig); + final MinIoArtifactInfo artifactInfo = new MinIoArtifactInfo(vspId, versionId); + final Attachment attachmentMock = mockAttachment("filename.csar", this.getClass().getResource("/files/sample-not-signed.csar")); + final byte[] attachmentBytes = attachmentMock.getObject(byte[].class); + artifactInfo.setBytes(attachmentBytes); + final ArgumentCaptor<Path> reduceTempDirectoryArg = ArgumentCaptor.forClass(Path.class); + when(packageSizeReducer.reduce(reduceTempDirectoryArg.capture())).thenReturn(attachmentBytes); + when(artifactStorageManager.upload(eq(vspId), eq(versionId), any(InputStream.class))).thenReturn(artifactInfo); + //when + Response response = orchestrationTemplateCandidate.upload(vspId, versionId, attachmentMock, user); + //then + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + assertTrue(((UploadFileResponseDto) response.getEntity()).getErrors().isEmpty()); + final Path actualReduceTempFolder = reduceTempDirectoryArg.getValue(); + final Path expectedReduceTempFolder = tempDir.resolve(Path.of(vspId, versionId)); + assertTrue(actualReduceTempFolder.startsWith(expectedReduceTempFolder), + String.format("Reduce temporary directory should be '%s'", expectedReduceTempFolder)); + } + @NotNull private VspUploadStatusDto createVspUploadStatus(final UUID lockId, final VspUploadStatus uploadStatus) { final VspUploadStatusDto vspUploadStatusProcessing = new VspUploadStatusDto(); @@ -204,7 +245,6 @@ class OrchestrationTemplateCandidateImplTest { @Test void uploadNotSignedArtifactStorageManagerIsEnabledTest() throws IOException { - when(storageFactory.createArtifactStorageManager()).thenReturn(artifactStorageManager); when(artifactStorageManager.isEnabled()).thenReturn(true); when(artifactStorageManager.getStorageConfiguration()).thenReturn(new MinIoStorageArtifactStorageConfig (true, new EndPoint("host", 9000, false), new Credentials("accessKey", "secretKey"), "tempPath", 10_000_000)); diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImplTest.java index f8af8df1ed..1936aaa7b4 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImplTest.java +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/VendorSoftwareProductsImplTest.java @@ -50,6 +50,7 @@ import org.openecomp.core.util.UniqueValueUtil; import org.openecomp.sdc.activitylog.ActivityLogManager; import org.openecomp.sdc.be.csar.storage.ArtifactStorageManager; import org.openecomp.sdc.common.errors.CoreException; +import org.openecomp.sdc.be.csar.storage.StorageFactory; import org.openecomp.sdc.itempermissions.PermissionsManager; import org.openecomp.sdc.notification.services.NotificationPropagationManager; import org.openecomp.sdc.vendorsoftwareproduct.VendorSoftwareProductManager; @@ -87,6 +88,8 @@ class VendorSoftwareProductsImplTest { private ArtifactStorageManager artifactStorageManager; @Mock private CatalogVspClient catalogVspClient; + @Mock + private StorageFactory storageFactory; @InjectMocks private VendorSoftwareProductsImpl vendorSoftwareProducts; @@ -103,6 +106,7 @@ class VendorSoftwareProductsImplTest { item.setType("vsp"); item.setId(vspId); when(itemManager.get(vspId)).thenReturn(item); + when(storageFactory.createArtifactStorageManager()).thenReturn(artifactStorageManager); } @Test |