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

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.

Related

How to optionally apply environment configuration?

I want to optionally apply a VPC configuration based on whether an environment variable is set.
Something like this:
custom:
vpc:
securityGroupIds:
- ...
subnetIds:
- ...
functions:
main:
...
vpc: !If
- ${env:USE_VPC}
- ${self:custom.vpc}
- ~
I'd also like to do similar for alerts (optionally add emails to receive alerts) and other fields too.
How can this be done?
I've tried the above configuration and a variety of others but just receive various different errors
For example:
Configuration error:
at 'functions.main.vpc': must have required property 'securityGroupIds'
at 'functions.main.vpc': must have required property 'subnetIds'
at 'functions.main.vpc': unrecognized property 'Fn::If'
Currently, the best way to achieve such behavior is to use JS/TS-based configuration instead of YAML. With TS/JS, you get full power of a programming language to shape your configuration however you want, including use of such conditional checks to exclude certain parts of the configuration. It's not documented too well, but you can use this as a starting point: https://github.com/serverless/examples/tree/v3/legacy/aws-nodejs-typescript
In general, you can do whatever you want, as long as you export a valid object (or a promise that resolves to a valid object) with serverless configuration.

Filter #ConfigurationProperties in #Value

I have my property file like this:
integrations:
- operationCode: CD001
connectionFactoryName: cf1
senderName: sn1
host: 192.168.1.1
port: 1416
queueManager: QM_TSTIN
channel: JAVA.CHANNEL
username: user
password: pass
receiveTimeout: 10000
sendQueue: SEND
receiveQueue: RECEIVE
How can i get my integration entity by operationCode? This is how i try to do this. But it does not work.
#Value("\${integrations.?[operationCode == 'CD001'].receiveQueue}")
var receiveQueue: String? = null
This is not possible using the property placeholder syntax ${} and also not possible by the SPEL syntax #{} as far as I know.
The property placeholder is a very simple solution it provides direct access ${a.b.c} with the option of default value ${a.b.c:default_value}. In terms of syntax that's it (javadoc).
The Spring Expression Language is very powerful comparing to the property placeholder but can not access properties directly. You can access a property by #{'${a.b.c}'} but the a.b.c must be a leaf in the yaml it can not access internal nodes. So you can not use the #{'${a.b}'.c} as the a.b is an internal node. So even you could use the #{bean.collection.?[attribute1 > 5].attribute2} in SPEL you can not use that for properties.
I suggest the standard yml attributes instead of the collection:
integrations:
CD001:
operationCode: CD001
receiveQueue: RECEIVER_2
CD002:
operationCode: CD002
receiveQueue: RECEIVER_2
and then use the #Value("${integrations.CD002.receiveQueue}") to get the value.
Unfortunately as far as I know there is no expression to solve your problem if you can not change the yaml's format.

Add list element to Spring properties at runtime

I have an application.yml file with the following:
topics:
input:
- name: topic1
partitions: 3
replicas: 1
- name: topic2
partitions: 6
replicas: 2
I would like to be able to update or add a new topic object at runtime.
I have tried the following for updating an existing object:
java -jar app.jar --topics.input[0].name="topicX"
and the following for adding another object to the list:
java -jar app.jar --topics.input[2].name="topicX" --topics.input[2].partitions=6 --topics.input[2].replicas=2
I am accessing the properties in the following way:
#Component
#ConfigurationProperties(prefix = "topics")
#Validated
public class TopicsConfiguration {
#NotEmpty
#NotNull
private List<TopicConfiguration> input = new ArrayList<>();
public List<TopicConfiguration> getInputTopics() {
return input;
}
public void setFacts(List<TopicConfiguration> input) {
this.input = input;
}
}
Where TopicConfiguration is just a POJO with the 3 fields listed.
When I don't try and modify any of the property objects at runtime this works exactly as I expect, however I can not update the property list at all. When I try and update an existing object I get an NPE. When I try and add a new object to the list I get:
Property: topics.input[2].name
Value: lmnop
Origin: "topics.input[2].name" from property source "commandLineArgs"
Reason: The elements [topics.input[2].name,topics.input[2].partitions,topics.input[2].replicas] were left unbound.
I would just like to know if there is any way to update or add an element to the list at runtime so that users of my project don't have to modify application.yml if they want to update this configuration.
okey so i did some research and some testing and came up with the following:
you cannot update a defined list in application.yml
if you run:
java -jar myApp.jar -my-list[2].name=foo
it will fail, because as soon as you want to pass the list the list will override the current list in application.yml and you are trying to pass in the 3rd item in the list when there is nothing at index 0 and 1.
it is not a dynamic list, it as an array.
You need to pass the entire list:
java -jar myApp.jar -my-list[0].name=foo -my-list[1].name=bar -my-list[2].name=foobar
So if you are going to pass a list in from cmd, you must always define the list from scratch.
So you want to reload your application.properties or application.yml in your Spring Boot application on the fly without having to make another code commit to another deployment? If I understood that right then here is how you can do it.
Read the these links and also google more on Spring Boot Actuator Reload Properties or Spring Scheduling. We can't provide you an out-written code to get this working but there is plenty of examples to examine and try out.
Links:
Actuator Refresh
Spring Scheduling
If I were you, I would prefer Actuator method as it avoids confusion and is cleaner.

Creating public and private Open API documentation from yaml file

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

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)

Resources