Creating public and private Open API documentation from yaml file - yaml

I have an OpenAPI document to describe some Endpoints. Some of those endpoints shall be public (visible for final users) and other private (visible to dev team only).
I'd like to know if is there a way to create only 1 yaml file containing all api methods and generate 2 docs page (1 for public endpoints and other for private). Is there a flag or configuration that would allow me to differ the endpoint type and make it visible or hidden?
I also need to do that on request level. There are some endpoints in which the request body has some "private" attributes (that final user shouldn't be aware of). Assuming that the first statement is true (there is a way I can create 2 api docs from a single YAML file), would it be possible to hide some request model attributes when applying the public alias documentation?

For those who are in doubt of how can this be accomplished, that's how I did:
I created a set of private keywords that are transparent for OpenApi (they are common strings)
I also created a python script that navigates through the yaml file and every time it finds one of these private keywords, it removes the yaml node it is contained.
Q: How did I manage to do that for endpoints?
A: I created in the "tags" section one called Private and all the private endpoints must refer to it. That's how I remove the private endpoints.
Q: How did I manage to do that for attributes?
A: In the attribute (or any element I want to make private) "description" tag, I add the keyword "Private". So if my script finds inside the description this string, the element is removed.
Of course that for the private OpenApi yaml file I create these tags I am using to generate the public version are still there. But since they are only strings, that's no big deal.
To navigate by the yaml file I used the following python lib: ruamel
So I keep recursively navigating through the file searching for OpenApi operations which the tag is called "Private" or the attributes which the description tag contains the char sequence "Private"

Instanciate a same OpenAPI file for several use context is a common need.
The classical solution is to copy-paste your files but in term of maintenability is not great!
Because I had the same need, I wrote my own tool "OpenAPI Dev Tool" (see github).
With this tool, you can design just one API and instanciate it for several contexts (private / public for example) without duplicate any lines or files (it uses EJS template engine).
For example, you can define a configuration file like that :
{
"folder": "./specs",
"specs": [
{
"file": "/petstore.yaml",
"context": {
private: true
}
},
{
"file": "/petstore.yaml"
"context": {
private: false
}
}
]
}
Where the same YAML file is specified but for 2 different contexts (public / private).
And in your petstore.yaml, you can have :
openapi: 3.0.0
...
components:
<%if (public) { %>
securitySchemes:
basicAuth:
type: http
scheme: basic
<% } %>
...
<%if (public) { %>
security:
- basicAuth: []
<% } %>
...
And the end, 2 specs will be produced from a same YAML file but for these 2 contexts :)

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);

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

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.

What is the use of star(*) in yaml file?

I was going through spring boot actuator when I stumbled upon this quote:
* has a special meaning in YAML, so be sure to add quotes if you want to include (or exclude) all endpoints.
I tried to look over the internet about it without any luck. What is the use of * in yaml file?
* is used to remove the repeated nodes. Consider this yaml example:
myprop:
uid: &id XXX
myprop1:
id: *id
The above will expand to:
myprop:
uid: XXX
myprop1:
id: XXX
Now try running this code:
#Value("${myprop.uid}") String uid;
#Value("${myprop1.id}") String id;
#Bean
ApplicationRunner runner() {
return args -> {
System.out.println(uid); // prints "XXX"
System.out.println(id); // prints "XXX"
System.out.println(uid.equals(id)); // prints "true"
};
}
From the spec:
Repeated nodes (objects) are first identified by an anchor (marked with the ampersand - “&”), and are then aliased (referenced with an asterisk - “*”) thereafter.
It depends on the context of the YAML file. You said you was going through spring boot actuator, so you can take a look into the reference documentation of Spring Boot, the chapter 5.2.2. Exposing Endpoints to be exact.
* can be used to select all endpoints. For example, to expose everything over HTTP except the env and beans endpoints, use the following properties:
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=env,beans
The asterisk * means that all the endpoints that belongs to a certain category are either included or exluded.
The sentence below just says that the asterisk * character must be quoted "*" in case of the YAML format usage over classic properties file.
* has a special meaning in YAML, so be sure to add quotes if you want to include (or exclude) all endpoints, as shown in the following example:
By the way, this sentence is the same one you have sited at your question.

How do I use the appProperties with the ruby api-client

I can't determine how to add custom properties or search for them.
Everything I have tried is giving me a Error - #<Google::Apis::ClientError: invalid: Invalid query> when I attempt to search for them. I can successfully complete other queries but I don't know if the client is setup to work with appProperties (or even properties at all).
Basically I just need the correct syntax for searching and adding since it doesn't appear to be in the documentation.
Assuming you already have a reference to an authorized DriveService, you can search based on appProperties using a q-parameter (documented here), like this:
file_list = drive.list_files(
q: "appProperties has { key='my_app_key' and value='my_val' }",
fields: 'files(id, name, appProperties)',
spaces: 'drive')
If you omit the fields parameter then the search will still work but the properties themselves won't be returned.
Updating appProperties is definitely arcane and the documentation is opaque. What you need is the ID of the file, and a File value object as a container for the attributes to update. Something like this:
new_app_properties = { 'my_app_key' => 'my_val' }
update_f = Google::Apis::DriveV3::File.new(app_properties: new_app_properties)
drive.update_file(file_id, update_f)

Is it Possible to have more than one messages file in Play framework

We have a site which will be used for two different clients. During first request the user will be asked to choose a client. Based on that text,labels and site content should be displayed.
Is it possible to have two messages file in Play framework and during session startup the messages file would be decided
As of my research we can have more than a file for each Locale, the messages will be get based on locale in the request.
No, it is not supported at the moment.
You can easily do that either in a plugin(Look at MessagesPlugin ) or even using a bootstrap job with the #onApplicationStartup annotation
// From MessagesPlugin.java
//default languange messages
VirtualFile appDM = Play.getVirtualFile("conf/messages");
if(appDM != null && appDM.exists()) {
Messages.defaults.putAll(read(appDM));
}
static Properties read(VirtualFile vf) {
if (vf != null) {
return IO.readUtf8Properties(vf.inputstream());
}
return null;
}
You can wrote you own PlayPlugin and handle implement play.PlayPlugin.getMessage(String, Object, Object...). Then you could choose the right file. The class play.i18n.Messages can be used as inspiration how to implement the method.
Solved this problem with below solution,
Created a class MessagesPlugIn which extends play.i18n.MessagesPlugin
Created a class Messages as like play.i18n.Messages
Had a static Map messaagesByClientID in Messages.java
Overridden onApplicationStart() in MessagesPlugIn
Loaded the Properties in messaagesByClientID as locales loaded in play.i18n.MessagesPlugin
Had a method get() in Messages.java, retrieve the property from messaagesByClientID based ClientId in the session. If the property is not available call get() in play.i18n.Messages
7.Created a Custom tag il8nTag and its used in HTML templates. il8nTag will invoke the methos in Messages.get().
Create your own Module based on play.api.i18n.I18nModule, but bound to your own implementation of MessagesApi, based on Play's DefaultMessagesApi (here is the part defining the files to load)
Then in your application.conf, disable Play's play.api.i18n.I18nModule and enable your own module.

Resources