I am trying to build my own micro services architecture and am stuck at the API.Gateway part.
I am trying to make Ocelot V14 find all configuration ocelot.json or configuration.json files inside the .sln file.
In the Program.cs file I am trying to merge configuration files with the following code from this link https://ocelot.readthedocs.io/en/latest/features/configuration.html#react-to-configuration-changes
builder.ConfigureServices(s => s.AddSingleton(builder))
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddOcelot(hostingContext.HostingEnvironment)
.AddEnvironmentVariables();
})
.UseStartup<Startup>();
When I run this the application creates the following ocelot.json file inside my OcelotApiGw Project
{
"ReRoutes": [
]
}
The problem is that it is empty and the reroutes do not work. When I paste the desired reroutes into this ocelot.json file the reroutes work, that is not the desired functionality I want.
What I want is to merge the configuration file automatically from different .json files.
Any help would be greatly appreciated.
How eShopOnContainers implements it this way with Ocelot V12
IWebHostBuilder builder = WebHost.CreateDefaultBuilder(args);
builder.ConfigureServices(s => s.AddSingleton(builder))
.ConfigureAppConfiguration(ic => ic.AddJsonFile(Path.Combine("configuration", "configuration.json")))
.UseStartup<Startup>();
In case you need more code, file structures or anything else just comment and ask.
Not sure if this is what you are looking for? This is the way Ocelot merges multiple routing files together
https://ocelot.readthedocs.io/en/latest/features/configuration.html#merging-configuration-files
We don't use this but this is how we have our startup defined:
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile(this.env.IsDevelopment() ? "ocelot.json" : "ocelot.octopus.json")
.AddEnvironmentVariables();
So we have our standard appSettings plus the Ocelot one we use that Octopus will transform various variables we want when our Ocelot instance is deployed out into our test/production envs (or just our test/local one).
This seems to be the bit that defines what to do for multiple files:
In this scenario Ocelot will look for any files that match the pattern
(?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you
want to set the GlobalConfiguration property you must have a file
called ocelot.global.json.
Not sure if you need to explicitly define each file (unless they can be defined via a variable like {env.EnvironmentName}) but that should be easy enough to test.
Sorry if I have got the wrong end of the stick but hope this helps.
Related
I am trying to make a POC and I'm such making a really simple use-case.
In there, I use a src/lib/db.ts who, for our interest, contains this code
console.log(import.meta.env.MONGO_URI, import.meta.env.SSR);
giving
undefined true
Of course, my .env file contains a definition for MONGO_URI, I tried with VITE_MONGO_URI and could see the value.
I know a way to expose it is to use VITE_MONGO_URI but my point is exactly not to expose it on the client-side.
I checked and the file db.ts is not bundled with the client, even the import.meta.env.SSR being true shows that the bundler knows it's happening on the server.
Question: How to access my private environment variables server-side ?
EDIT: As specified by Shriji Kondan, the API for this purpose has been created now : here
You could use dotenv on the server side, assuming you are using node-adapter, you can have a file _constants.ts in your app
import 'dotenv/config';
export const MONGO_URI = process.env.MONGO_URI;
and then import this variable into your script.
It's not very awesome to put secrets on client-side code. It should be either utilities.ts with a performed action SUPER_SECRET_API_KEY="$ecret#p1Key" in .env file, then request it via in src/lib/utilities/utility.js as explained here :
import { SUPER_SECRET_API_KEY } from '$env/static/private';
export function performApiAction() {
const apiInstance = initialiseApi({key: SUPER_SECRET_API_KEY});
}
or from page.server.ts via form actions as stated here which is preferable way but it's more complex.
I am trying to add a custom favicon to my NestJs documentation. However, I am a bit lost on how the path file gets resolved and not sure how to achieve this.
I am using nestjs/swagger module version 3.1.0 and trying to pass the path file like so when initializing the Swagger Module.
My main.ts file
SwaggerModule.setup('/v1/docs', app, document, {
customCss: CUSTOM_STYLE,
customSiteTitle: 'My API Documentation',
customfavIcon: './public/favicon.jpg'
});
Searched on the github issues and didn't find anything useful. And as you can see from the code I was able to modify the CSS styles, but I cannot figure out how to make the favicon custom.
Appreciate any help
I have added the custom favicon to my swagger docs using following:
The first thing you make sure is, in your main.ts, the app is initialized with the following:
const app: NestExpressApplication = await NestFactory.create(...)
To serve static content you must initialize your app with NestExpressApplication.
The next thing is to allow the Nest application to look for public content using the following in your main.ts after initialization:
app.useStaticAssets(join(__dirname, '..', 'public'));
Also, create a public directory in your root of the application and paste your favicon.jpg file in it.
Now its time to initialize the Swagger in main.ts
SwaggerModule.setup('/v1/docs', app, document, {
customCss: CUSTOM_STYLE,
customSiteTitle: 'My API Documentation',
customfavIcon: '../favicon.jpg'
});
You must give a relative path to the root of the application like ../favicon.jpg in case our main.ts is in src folder in root of the application.
Alternative solution, just host your favicon and reference it with external url
SwaggerModule.setup('api', app, getSwaggerDocument(app), {
...
customfavIcon:
'https://[your-bucket-url].com/.../anything.png',
});
To iterate on pravindot17's answer, now there's the #nestjs/serve-static package for hosting static files. Which avoid us from type-casting the Nest.js client and relying on our implicit assumption that we're running an Express-backed Nest.js server.
After installing the package, you hook it into your src/app.module.ts. This configuration expects that the root of your project has a /public/ folder where you store your static assets.
import { Module } from '#nestjs/common';
import { ServeStaticModule } from '#nestjs/serve-static';
import { join } from 'path';
#Module({
imports: [
// Host static files in ../public under the /static path.
ServeStaticModule.forRoot({
/**
* Config options are documented:
* https://github.com/nestjs/serve-static/blob/master/lib/interfaces/serve-static-options.interface.ts
*/
rootPath: join(__dirname, '..', '..', 'public'),
serveRoot: '/static',
}),
// ...
})
export class AppModule {}
Now my own preference is using an absolute path rather than relative, as it makes it independent from the path we picked to host our API documentation under.
SwaggerModule.setup('/v1/docs', app, document, {
customfavIcon: '/static/favicon.jpg'
});
One last note is that this configuration hosts static files from /static/*, this is done to prevent that API calls to non-existing endpoints show an error message to the end-user that the static file cannot be found.
Otherwise, all 404's on non-existing endpoints will look something like:
{"statusCode":404,"message":"ENOENT: no such file or directory, stat '/Users/me/my-project/public/index.html'"}
Is there an easy way to get hold of a path object so I can check if a given label path exists. Say for example if path.exists("#external_project_name//:filethatmightexist.txt"):. I can see that the repository context has this. But I need to have a wrapping repository rule. Is it possible to do this in a macro or Skylark native call instead?
Even with a repository_rule, I had a lot of trouble with this due to what you already pointed out:
if you create a Label with a path that doesn't exist, it will cause the build to fail
But if you're willing to do a repository rule, here's a possible solution...
In this example, my rule allows specification of a default configuration if a config file is not present. The configuration can be checked into .gitignore and overridden for individual developers, but work out of the box for most cases.
I think I understand why the ctx.actions have sibling arguments now, same idea here. The trick is config_file_location is a true label, and then config_file is a string attribute. I chose BUILD arbitrarily, but since all workspaces have a top level BUILD that's public seemed legit-ish.
WORKSPACE Definition
...
workspace(name="E02_mysql_database")
json_datasource_configuration(name="E02_datasources",
config_file_location="#E02_mysql_database//:BUILD",
config_file="database.json")
The definition for json_datasource_configuration looks like this:
json_datasource_configuration = repository_rule(
attrs = {
"config_file_location": attr.label(
doc="""
Path relative to the repository root for a datasource config file.
"""),
"config_file": attr.string(
doc="""
Config file, maybe absent
"""),
"default_config": attr.string(
# better way to do this?
default="None",
doc = """
If no config is at the path, then this will be the default config.
Should look something like:
{
"datasource_name": {
"host": "<host>"
"port": <port>
"password": "<password>"
"username": "<username>"
"jdbc_connection_string": "<optional>"
}
}
There can be more than datasource configured... maybe, eventually.
""",
),
},
local = True,
implementation = _json_config_impl,
)
Then in the rule I can test for the file existence, and if not present, do other logic.
def _json_config_impl(ctx):
"""
Allows you to specify a file on disk to use for data connection.
If you pass a default
"""
config_path = ctx.path(ctx.attr.config_file_location).dirname.get_child(ctx.attr.config_file)
config = ""
if config_path.exists:
config = ctx.read(config_path)
elif ctx.attr.default_config == "None":
fail("Could not find config at %s, you must supply a default_config if this is intentional" % ctx.attr.config_file)
else:
config = ctx.attr.default_config
...
probably too late to help, but your question is the only thing I found referencing this goal. If someone knows a better way I am looking for other options. It's complicated to explain to other developers why the rule has to work the way it does.
Also note, if you change the config file, you have to clean to get the workspace to re-read the config. I haven't been able to figure out any way to fix that. glob() does not work in the workspace.
As I am trying to write a Grails Plugin, I stumbled upon two problems:
how do I modify one of the configuration files like Config.groovy or DataSource.groovy from witin the _install.groovy script? It is easy to append something to those files, but how do I modify it in a clean way? text.replaceAll()? Or should I create a new config file?
how do I get the name of the current application into which the plugin will be installed? I tried to use app.name and appName but both do not work.
Is there maybe somewhere a good tutorial on creating plugins which I haven't found yet?
Here is an example of editing configuration files from scripts/_Install.groovy.
My plugin copies three files to the target directory.
.hgignore is used for version control,
DataSource.groovy replaces the default version, and
SecurityConfig.groovy contains extra settings.
I prefer to edit the application's files as little as possible, especially because I expect to change the security setup a few years down the road. I also need to use properties from a jcc-server-config.properties file which is customized for each application server in our system.
Copying the files is easy.
println ('* copying .hgignore ')
ant.copy(file: "${pluginBasedir}/src/samples/.hgignore",
todir: "${basedir}")
println ('* copying SecurityConfig.groovy')
ant.copy(file: "${pluginBasedir}/src/samples/SecurityConfig.groovy",
todir: "${basedir}/grails-app/conf")
println ('* copying DataSource.groovy')
ant.copy(file: "${pluginBasedir}/src/samples/DataSource.groovy",
todir: "${basedir}/grails-app/conf")
The hard part is getting Grails to pick up the new configuration file. To do this, I have to edit the application's grails-app/conf/Config.groovy. I will add two configuration files to be found on the classpath.
println ('* Adding configuration files to grails.config.locations');
// Add configuration files to grails.config.locations.
def newConfigFiles = ["classpath:jcc-server-config.properties",
"classpath:SecurityConfig.groovy"]
// Get the application's Config.groovy file
def cfg = new File("${basedir}/grails-app/conf/Config.groovy");
def cfgText = cfg.text
def appendedText = new StringWriter()
appendedText.println ""
appendedText.println ("// Added by edu-sunyjcc-addons plugin");
// Slurp the configuration so we can look at grails.config.locations.
def config = new ConfigSlurper().parse(cfg.toURL());
// If it isn't defined, create it as a list.
if (config.grails.config.locations.getClass() == groovy.util.ConfigObject) {
appendedText.println('grails.config.locations = []');
} else {
// Don't add configuration files that are already on the list.
newConfigFiles = newConfigFiles.grep {
!config.grails.config.locations.contains(it)
};
}
// Add each surviving location to the list.
newConfigFiles.each {
// The name will have quotes around it...
appendedText.println "grails.config.locations << \"$it\"";
}
// Write the new configuration code to the end of Config.groovy.
cfg.append(appendedText.toString());
The only problem is adding SecurityConfig.groovy to the classpath. I found that you can do that by creating the following event in the plugin's /scripts/Events.groovy.
eventCompileEnd = {
ant.copy(todir:classesDirPath) {
fileset(file:"${basedir}/grails-app/conf/SecurityConfig.groovy")
}
}
Ed.
You might try changing the configuration within the MyNiftyPlugin.groovy file (assuming that your plugin is named my-nifty). I've found that I can change the configuration values within the doWithApplicationContext closure. Here's an example.
def doWithApplicationContext = { applicationContext ->
def config = application.config;
config.edu.mycollege.server.name = 'http://localhost:8080'
config.edu.mycollege.server.instance = 'pprd'
}
The values you enter here do show up in the grailsApplication.config variable at run time. If it works for you, it will be a neater solution, because it doesn't require changes to the client project.
I must qualify that with the fact that I wasn't able to get Spring Security to work by this technique. I believe that my plugin (which depends on Spring Security) was loaded after the security was initialized. I decided to add an extra file to the grails-app/conf directory.
HTH.
For modifying configuration files, you should use ConfigSlurper:
def configParser = new ConfigSlurper(grailsSettings.grailsEnv)
configParser.binding = [userHome: userHome]
def config = configParser.parse(new URL("file:./grails-app/conf/Config.groovy"))
If you need to get application name from script, try:
metadata.'app.name'
Grails 1.3.7
I have some configuration located in an external config file. One of the entires looks like this:
site.maintenance.mode = false
I have a filter which checks for certain config settings for specific URLs. When I do a run-app or deploy a WAR into Tomcat and do:
boolean maintenanceMode = grailsApplication.config.site.maintenance.mode
maintenanceMode is coming back true. If I look at the config object in debug mode, this is what I get:
site={maintenance={mode=false, message="<p>Our trail guides are working hard to get the system back on track.</p><p>We're sorry, the account system is down for maintenance at the moment. We'll get it back online as quickly as we can. Thanks for your patience.</p>"}}
I have a controller that I use to reload this config file dynamically and hitting this controller will fix the issue. But I'm curious as to why it is incorrect on first runs and why the discrepency in what is getting put in the maintenanceMode variable vs what is actually in the config object.
Are you using a Java properties file or a Groovy file? If you're using a properties file then I believe Grails will interpret site.maintenance.mode=false the same way as site.maintenance.mode='false' and since Groovy will interpret:
"false".asBoolean() == true
then that would explain why you would see that initial true value.
I just ran a simple test locally to verify this behavior. When I externalize my properties in a file called test.properties then site.maintenance.mode=false initially gets a boolean value of true, when I use a file called test.groovy then it interprets the boolean value of site.maintenance.mode=false as false. I believe this is because when you use a Groovy file Grails uses ConfigurationSlurper to process it but when you use a properties file Grails interprets everything as String name/value pairs.
What I do is to have an external Config.groovy file, for instance: MyConfig.groovy
At the end of the standard grails Config.groovy file, I have the following:
def ENV_NAME = "MY_EXTERNAL_CONFIG"
if(!grails.config.locations || !(grails.config.locations instanceof List)) {
grails.config.locations = []
}
if(System.getenv(ENV_NAME)) {
grails.config.locations << "file:" + System.getenv(ENV_NAME)
} else if(System.getProperty(ENV_NAME)) {
grails.config.locations << "file:" + System.getProperty(ENV_NAME)
} else {
println "No external Configs found."
}
So now you can have a MyConfig.groovy file anywhere in production environment (for example) and then set an Environment system variable to point to this file (or pass it as parameter to startup.sh), before you start tomcat:
MY_EXTERNAL_CONFIG="/home/tomcat/configs/MyConfig.groovy"
export MY_EXTERNAL_CONFIG
That's it. Now you have an external MyConfig.groovy file. The properties in it are accessible from your grails app as they were part of the standard Config.groovy
import org.codehaus.groovy.grails.commons.*
//...
ConfigurationHolder.config.foo.bar.hello