Cloudinary Error: {"error":{"message":"Missing required parameter - timestamp"}} - spring

I am trying to use Cloudinary's .downloadMulti(String tag, Map options) to generate a URL to download multiple images as a zip with the same tag. I am generating the URL seemingly just fine, but when I go to the URL, I am met with {"error":{"message":"Missing required parameter - timestamp"}}.
I've researched a bit and I saw that I need to sign the request, but it isn't saying that I'm missing that - just the timestamp. I believe the request is already being signed, just need a proper timestamp. I believe it needs to be within the constructor, but when I called Util.timestamp() it isn't recognized as a reference.
My Cloudinary initializer:
private final Cloudinary cloudinary = new Cloudinary(ObjectUtils.asMap(
"cloud_name", "dxoa7bbix",
"api_key", "161649288458746",
"api_secret", "..."));
My upload method:
public Photo uploadOrderImage(String imageURL, String publicId, Order order, String photoType) throws IOException {
Map result = cloudinary.uploader().upload(new File(imageURL), ObjectUtils.asMap(
"public_id", publicId,
"tags", order.getId().toString()));
Photo sellOrderPhoto = new Photo(
result.get("secure_url").toString(),
photoType,
order
);
return photoRepository.save(sellOrderPhoto);
}
This is my download method:
public String downloadPhotos(String tag) throws IOException {
return cloudinary.downloadMulti(tag, ObjectUtils.asMap(
"tags", tag
));
}
An example URL my download method returns: Generated URL: https://api.cloudinary.com/v1_1/dxoa7bbix/image/multi?mode=download&async=false&signature=5f5da549fc78ea3fd50f034cdc76e2cce3089d48&api_key=161649288458746&tag=137×tamp=1638583257.
Overall, I think the problem is the lack of a timestamp. If you have any ideas, that would be great!

The reason for the error is that the parameter is expected to be called timestamp but based on the URL you shared, it is actually ×tamp.
If you want to generate a URL to a ZIP file containing assets that share a particular tag, then you will want to use the generate_archive method and not multi which provides different functionality.
If you replace the following code:
return cloudinary.downloadMulti(tag, ObjectUtils.asMap(
"tags", tag
));
With:
return cloudinary.downloadZip(ObjectUtils.asMap(
"tags", tag,
"resource_type", "image")
);
Then that will generate and return a URL to a ZIP file that will get created when the URL is accessed and contain images from your cloud that contain the tag you specified.
The Cloudinary Java SDK you're using will handle the signature/timestamp generation automatically when using any of the built-in methods, therefore, you don't need to make any changes to the SDK code or calculate the signature yourself if using the built-in methods. Signature generation is only necessary if you're not intending to use any of the SDKs but integrate with the Cloudinary API using your own custom code - such as if you're using a language for which a Cloudinary SDK doesn't yet exist. In such cases, if you want to perform authenticated API calls, you will need to generate authentication signatures yourself.

Related

Download file contents and names into a List with Apache Camel FTP

I would like to download a list of files with name and content in Apache Camel.
Currently I am downloading the file content of all files as byte[] and storing them in a List. I then read the list using a ConsumerTemplate.
This works well. This is my Route:
from(downloadUri)
.aggregate(AggregationStrategies.flexible(byte[].class).accumulateInCollection(
LinkedList.class))
.constant(true)
.completionFromBatchConsumer()
.to("direct:" + this.destinationObjectId);
I get the List of all downloaded file contents as byte[] as desired.
I would like to extend it now so that it downloads the content and the file name of each file. It shall be stored in a pair object:
public class NameContentPair {
private String fileName;
private byte[] fileContent;
public NameContentPair(String fileName, byte[] fileContent) { ... }
}
These pair objects for each downloaded file shall in turn be stored in a List. How can I change or extend my Route to do this?
I tried Camel Converters, but was not able to build them properly into my Route. I always got the Route setup wrong.
I solved this by implementing a custom AggregationStrategy.
It reads the file name and the file content from each Exchange and puts them into a list as a NameContentPair objects. The file content and file name is present in the Exchange's body as a RemoteFile and is read from there.
The general aggregation implementation is based on the example implementation from https://camel.apache.org/components/3.15.x/eips/aggregate-eip.html
The aggregation strategy is then added to the route
from(downloadUri)
.aggregate(new FileContentWithFileNameInListAggregationStrategy())
.constant(true)
.completionFromBatchConsumer()
.to("direct:" + this.destinationObjectId);

spring-integration-aws dynamic file download

I've a requirement to download a file from S3 based on a message content. In other words, the file to download is previously unknown, I've to search and find it at runtime. S3StreamingMessageSource doesn't seem to be a good fit because:
It relies on polling where as I need to wait for the message.
I can't find any way to create a S3StreamingMessageSource dynamically in the middle of a flow. gateway(IntegrationFlow) looks interesting but what I need is a gateway(Function<Message<?>, IntegrationFlow>) that doesn't exist.
Another candidate is S3MessageHandler but it has no support for listing files which I need for finding the desired file.
I can implement my own message handler using AWS API directly, just wondering if I'm missing something, because this doesn't seem like an unusual requirement. After all, not every app just sits there and keeps polling S3 for new files.
There is S3RemoteFileTemplate with the list() function which you can use in the handle(). Then split() result and call S3MessageHandler for each remote file to download.
Although the last one has functionality to download the whole remote dir.
For anyone coming across this question, this is what I did. The trick is to:
Set filters later, not at construction time. Note that there is no addFilters or getFilters method, so filters can only be set once, and can't be added later. #artem-bilan, this is inconvenient.
Call S3StreamingMessageSource.receive manually.
.handle(String.class, (fileName, h) -> {
if (messageSource instanceof S3StreamingMessageSource) {
S3StreamingMessageSource s3StreamingMessageSource = (S3StreamingMessageSource) messageSource;
ChainFileListFilter<S3ObjectSummary> chainFileListFilter = new ChainFileListFilter<>();
chainFileListFilter.addFilters(
new S3SimplePatternFileListFilter("**/*/*.json.gz"),
new S3PersistentAcceptOnceFileListFilter(metadataStore, ""),
new S3FileListFilter(fileName)
);
s3StreamingMessageSource.setFilter(chainFileListFilter);
return s3StreamingMessageSource.receive();
}
log.warn("Expected: {} but got: {}.",
S3StreamingMessageSource.class.getName(), messageSource.getClass().getName());
return messageSource.receive();
}, spec -> spec
.requiresReply(false) // in case all messages got filtered out
)

HAPI FHIR Json encoder not handling contained resources

I have a FHIR Device resource that contains a FHIR DeviceComponent resource. I use the following HAPI FHIR code to 'insert' one resource into the other:
protected static void insertResourceInResouce(BaseResource resource, BaseResource resourceToInsert)
{
ContainedDt containedDt = new ContainedDt();
ArrayList<IResource> resourceList = new ArrayList<IResource>();
resourceList.add(resourceToInsert);
containedDt.setContainedResources(resourceList);
resource.setContained(containedDt);
}
According to the Eclipse debugger the insertion works fine. This resource with its insertion is then added to a bundle. When all the work is done the Eclipse debugger shows the resource with the contained resource properly placed in the bundle. However, when generating a JSON string the contained resources are not there. The encoding operation appears as follows:
return fhirContext.newJsonParser().setPrettyPrint(true)
.encodeResourceToString(bundle);
Any ideas what I am doing wrong?
It turns out that one must reference the contained resource from the parent resource using the "#" to prefix the reference. If one does that then the contained resource will be present in the XML and JSON.
Admittedly this requirement makes no sense to me. Why would I include a resource INSIDE another scoping resource if I did not think it was important?

Watch for updated properties in Wicket

In my current project we need to implement a way for texters to manage the wicket messages/internationalization via upload of property files.
Also see this question: Administrating internationalized wicket applications
As suggested there, I've implemented a custom IStringResourceLoader and added it at the beginning of the StringResourceLoader list to override any properties already in place:
getResourceSettings().getStringResourceLoaders().add(0, new CustomStringResourceLoader());
This however is not enough, because updates can happen and need to be loaded at runtime. StringResources are cached by wicket and updated only when the ResourceWatcher is triggered.
I found where Wicket adds the string resources to the watcher: the PropertiesFactory in the settings. The method to add a resource to the watcher is addToWatcher(...). However this method is protected and also the whole setup suggests this is used for development purposes and not for production.
I managed to use this method by extending PropertiesFactory and effectively creating a custom version to add to settings:
getResourceSettings().setPropertiesFactory(new CustomPropertiesFactory(getResourceSettings()));
getResourceSettings().setResourcePollFrequency(Duration.seconds(1));
So my Question is: I feel this is quite the circuitious solution. Is there another way to watch for changing properties files?
My solution to the problem:
getResourceSettings().getStringResourceLoaders().add(0, new CustomResourceLoader());
getResourceSettings().getResourceFinders().add(new Path("/pathToResources"));
getResourceSettings().setResourcePollFrequency(Duration.seconds(1));
This inserts my CustomResourceLoader at the beginning of the list so all properties are first checked there.
The added Path tells the PropertiesFactory to look for resources in a given arbitrary directory outside of wicket.
I needed custom names for my resource files as well, I realized this in the CustomResourceLoader:
public String loadStringResource(final Class<?> clazz, final String key, final Locale locale, final String style, final String variation) {
final String myResourceFilename = createCustomResourceFileName(locale);
final IPropertiesFactory pF = Application.get().getResourceSettings().getPropertiesFactory();
final org.apache.wicket.resource.Properties props = pF.load(clazz, myResourceFilename);
...
}
When using the PropertiesFactory to load the files, it adds them to the internal IModificationWatcher automatically.
It turns out that part of the problem was, that the resource files are in a non-standard encoding. This can be fixed by adding a special IPropertyLoader to the PropertiesFactory in the settings:
((PropertiesFactory) getResourceSettings().getPropertiesFactory()).getPropertiesLoaders().add(0,
new UtfPropertiesFilePropertiesLoader("properties", "your-favorite-encoding"));

SharpGS how to download file?

I am using SharpGS for google cloud storage. I could upload file using the
GetBucket("some-bucket").AddObject() method but I could not download the file using the following code
GetBucket("some-bucket").GetObjectHead("some-file").Content
It gave me null value for the byte return
any idea?
thanks
The GetObjectHead looks up the object using a HEAD request, so it doesn't retrieve the content.
If you take a look at the demo code, you can retrieve object contents by listing the bucket:
var bucket = GetBucket("some-bucket");
foreach (var o in bucket.Objects) {
Console.WriteLine(Encoding.UTF8.GetString(o.Retrieve().Content));
}
There doesn't seem to be a way to get an IObject without listing the bucket. I would suggest adding a method to the IObjectContent class returned from GetObjectHead to fetch the IObject. The project is on GitHub.

Resources