@showroomprive/azure-appconfig
Version:
A library to use Azure App Configuration in NestJS
219 lines • 9.51 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AzureAppconfigService = void 0;
const appConfig = __importStar(require("@azure/app-configuration"));
const common_1 = require("@nestjs/common");
const azureIdentity = __importStar(require("@azure/identity"));
const keyvault_secrets_1 = require("@azure/keyvault-secrets");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
let AzureAppconfigService = class AzureAppconfigService {
constructor() {
this.generalKeyName = "SRP";
this.appConfiguration = null;
this.credentials = new azureIdentity.DefaultAzureCredential();
if (process.env.ENVIRONMENT?.toLowerCase() !== 'local') {
if (this.isConnectionString(process.env.APP_CONFIG_URL)) {
this.appConfigClient = new appConfig.AppConfigurationClient(process.env.APP_CONFIG_URL ?? "");
}
else {
this.appConfigClient = new appConfig.AppConfigurationClient(process.env.APP_CONFIG_URL ?? "", this.credentials);
}
}
}
async initialize() {
let appConfig;
if (process.env.ENVIRONMENT?.toLowerCase() === 'local') {
appConfig = this.loadLocalConfig();
}
else {
appConfig = await this.get();
}
return appConfig;
}
loadLocalConfig() {
const filePath = path.resolve(process.cwd(), 'local.config.json');
try {
const configFile = fs.readFileSync(filePath, 'utf-8');
const configData = JSON.parse(configFile);
const generalConfig = configData[this.generalKeyName] || {};
const appConfigName = process.env.APP_CONFIG_NAME ?? "";
const applicationConfig = configData[appConfigName] || {};
return {
General: generalConfig,
Application: applicationConfig,
};
}
catch (error) {
if (error.code === 'ENOENT') {
console.error(`Local config file not found, looking for path : ${filePath}.`);
}
throw error;
}
}
async get() {
const appConfigKey = process.env.APP_CONFIG_NAME;
this.appConfiguration = {};
const settings = await this.fetchSettings(appConfigKey);
await this.processSettings(settings);
return this.transformListToNestedConfig(this.appConfiguration);
}
async processSettings(settings) {
for await (const setting of settings) {
const keyExist = setting.key in (this.appConfiguration ?? {});
if (keyExist && !setting.label) {
continue;
}
if (appConfig.isSecretReference(setting)) {
await this.processSecretReference(setting);
}
else if (this.appConfiguration !== null) {
this.appConfiguration[setting.key] = setting.value;
}
}
}
async processSecretReference(setting) {
const parsed = appConfig.parseSecretReference(setting);
const { name, vaultUrl } = (0, keyvault_secrets_1.parseKeyVaultSecretIdentifier)(parsed.value.secretId);
if (name) {
const vaultClient = new keyvault_secrets_1.SecretClient(vaultUrl, this.credentials);
const sVal = await vaultClient.getSecret(name);
if (this.appConfiguration !== null) {
this.appConfiguration[setting.key] = sVal.value;
}
}
}
transformListToNestedConfig(appConfig) {
const result = {
General: {},
Application: {}
};
Object.entries(appConfig).forEach(([key, value]) => {
const keys = key.split(':');
let currentLevel;
if (keys[0] === this.generalKeyName) {
currentLevel = result.General;
}
else {
currentLevel = result.Application;
}
for (let i = 1; i < keys.length - 1; i++) {
const currentKey = keys[i];
const maybeIndex = parseInt(keys[i + 1], 10);
const isNextAnIndex = !isNaN(maybeIndex);
if (typeof currentLevel === 'object' && currentLevel !== null) {
const currentObject = currentLevel;
if (isNextAnIndex && !Array.isArray(currentObject[currentKey])) {
currentObject[currentKey] = [];
}
else if (!isNextAnIndex && typeof currentObject[currentKey] !== 'object') {
currentObject[currentKey] = {};
}
currentLevel = currentObject[currentKey];
}
else {
throw new Error(`Unexpected structure at key: ${currentKey}`);
}
}
const lastKey = keys[keys.length - 1];
const maybeIndex = parseInt(lastKey, 10);
if (typeof currentLevel === 'object' && currentLevel !== null) {
const currentObject = currentLevel;
if (!isNaN(maybeIndex) && Array.isArray(currentObject)) {
currentObject[maybeIndex] = value;
}
else {
currentObject[lastKey] = value;
}
}
else {
throw new Error(`Cannot assign value to non-object at key: ${lastKey}`);
}
});
return result;
}
isConnectionString(url) {
if (!url)
return false;
const connectionStringPattern = /Endpoint=.*;Id=.*;Secret=.*/;
return connectionStringPattern.test(url);
}
async fetchSettings(appConfigKey) {
const generalSettings = this.appConfigClient?.listConfigurationSettings({
keyFilter: `${this.generalKeyName}:*`
});
const labeledGeneralSettings = this.appConfigClient?.listConfigurationSettings({
keyFilter: `${this.generalKeyName}:*`,
labelFilter: `${process.env.ENVIRONMENT ?? ''}`
});
const settings = this.appConfigClient?.listConfigurationSettings({
keyFilter: `${appConfigKey}:*`
});
const labeledSettings = this.appConfigClient?.listConfigurationSettings({
keyFilter: `${appConfigKey}:*`,
labelFilter: `${process.env.ENVIRONMENT ?? ''}`
});
const settingsMap = new Map();
for await (const setting of generalSettings ?? []) {
settingsMap.set(setting.key, setting);
}
for await (const setting of settings ?? []) {
settingsMap.set(setting.key, setting);
}
for await (const labeledSetting of labeledSettings ?? []) {
settingsMap.set(labeledSetting.key, labeledSetting);
}
for await (const labeledSetting of labeledGeneralSettings ?? []) {
settingsMap.set(labeledSetting.key, labeledSetting);
}
return Array.from(settingsMap.values());
}
};
exports.AzureAppconfigService = AzureAppconfigService;
exports.AzureAppconfigService = AzureAppconfigService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [])
], AzureAppconfigService);
//# sourceMappingURL=azure-appconfig.service.js.map