How to use expire_time parameter with the Node.js node-oracledb? - oracle19c

Sample Code
try {
const oracledb = require('oracledb');
const express = require('express');
const app = express();
// Memorized Connections
var allDBSessions = {};
app.get('/', (req, res) => {
const dbConfig = {
user: 'vph_dev_ad_gss_tran',
password: 'Gsstpl100'
// ,connectString: '//IP:PORT/SERVICE_NAME' // Working as Expected
,connectString: '//IP:PORT/SERVICE_NAME?expire_time=1' // Minutes // Not Working
// ,connectString: '//IP:PORT/SERVICE_NAME?connect_timeout=15' // Seconds // Not Working
// Your connection string could be "mydbmachine.example.com/orclpdb1?expire_time=2" // Sample From Node Oracledb Site
};
async function ExecuteQuery() {
try {
// Getting DB Connections
let dbInstance;
if (allDBSessions.first_instance) {
// Assigning From Memorized Connections
console.log('Connection Available From the Memory');
console.log('Connection Created Time - ' + allDBSessions.first_instance.created_time);
dbInstance = allDBSessions.first_instance.db_connection;
console.log('Connection Assigned From Memory');
} else {
//Creating New DB Instance
console.log('Creating New Connection');
dbInstance = await oracledb.getConnection(dbConfig);
console.log('Connection Created Successfully');
// Memorizing the DB Instance
allDBSessions.first_instance = {
db_connection: dbInstance,
created_time: new Date().toLocaleString()
}
console.log('Connection Stored into Memory');
}
// Executing Query
var query = 'select 1 from dual';
console.log('Executing the Query - ' + query);
let queryResult = await dbInstance.execute(query);
console.log('queryResult', queryResult);
var finalResult = {
'Query Result': queryResult,
'Usedd DB Session Info': allDBSessions.first_instance
};
res.send(finalResult); // Sending Response
} catch (error) {
console.log(error, '----- Catch Error in ExecuteQuery()-----');
res.send(error);
}
}
ExecuteQuery();
})
app.listen(3005, () => {
console.log(`Oracle DB Sample With Expire Timeout`);
console.log(`server is listening on PORT 3005`);
})
} catch (error) {
console.log(error, '----- Catch Error -----');
}
Above Sample Throwing Error while trying to make a successful Connection with the Oracle Db which is 19c.
Error Shown below
[Error: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor] {
errorNum: 12514,
offset: 0
}
Please guide me to overcome from this issue...
Thanks In AdvancE...
HaPPy CodinG...

The Easy Connect Plus syntax like '//IP:PORT/SERVICE_NAME?expire_time=1' is usable when the Oracle Client libraries that node-oracledb uses are 19c or later. The database version doesn't matter. See the architecture diagram in the documentation.
Check what client libraries node-oracledb is using by running the example app version.js. It will output something like:
$ node version.js
Run at: Thu Mar 18 2021 08:17:22 GMT+1100 (Australian Eastern Daylight Time)
Node.js version: v12.21.0 (darwin x64)
Node-oracledb version: 5.2.0-dev
Oracle Client library version: 19.8.0.0.0
Oracle Database version: 19.3.0.0.0
Check your Oracle Client library version is 19 or later.
Update on EXPIRE_TIME:
With 18c client libraries it can be added as (EXPIRE_TIME=n) to the DESCRIPTION section of a connect descriptor (in a full connect descriptor string in the app, or in the tnsnames.ora file).
With 19c client libraries it can be used via Easy Connect: host/service?expire_time=n.
With 21c client libraries it can be used in a client-side (i.e. Node.js machine) sqlnet.ora.

Related

Why won't unit tests connect to a websocket

UPDATE: I've uploaded a repo - https://github.com/mrpmorris/CannotIntegrationTestWebApp/blob/master/TestProject1/UnitTest1.cs
I have a web server that serves both HTTPS and WebSocket requests. When I run the app I am able to connect and make requests from postman for both HTTPS://localhost:8080 and WSS://localhost:8080/game-server
using Gambit.ApplicationLayer;
using Gambit.GameServer.Configuration;
using Gambit.GameServer.UseCases;
namespace Gambit.GameServer;
public class Program
{
public static async Task Main(string[] args)
{
WebApplication app = BuildApp(args);
await RunAppAsync(app);
}
public static WebApplication BuildApp(string[] args, Action<WebApplicationBuilder>? configure = null)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IServiceCollection services = builder.Services;
IConfiguration configuration = builder.Configuration;
IWebHostEnvironment environment = builder.Environment;
services.AddControllers();
services.AddLogging(opts =>
{
opts.ClearProviders();
opts.AddConfiguration(configuration.GetSection("Logging"));
opts.AddDebug();
opts.AddEventSourceLogger();
#if DEBUG
if (environment.IsDevelopment())
opts.AddConsole();
#endif
});
services.Configure<GameServerOptions>(configuration.GetSection("GameServer"));
services.AddApplicationServices(configuration);
configure?.Invoke(builder);
WebApplication app = builder.Build();
return app;
}
public static async Task RunAppAsync(WebApplication app)
{
app.MapGet("/", () => "Gambit.Server.API is running");
app.AddUserUseCases();
app.AddGameUseCases();
app.MapControllers();
app.UseWebSockets();
await app.RunAsync();
}
}
When I run my unit tests I use the same code to create and run the server (once per test run) my tests are able to make HTTPS requests but not connect via a WebSocket. When I try, I get a 404 error. I experience the same in PostMan.
static IntegrationTestsServer()
{
ConfigureMocks();
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "IntegrationTesting");
var app = Program.BuildApp(Array.Empty<string>(), builder =>
{
builder.WebHost.UseSetting("urls", "https://localhost:8080");
});
Configuration = app.Services.GetRequiredService<IConfiguration>();
GameServerOptions = app.Services.GetRequiredService<IOptions<GameServerOptions>>();
var dbContextOptions = app.Services.GetRequiredService<DbContextOptions<ApplicationDbContext>>();
using var dbContext = new ApplicationDbContext(dbContextOptions);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
HttpClient = new HttpClient { BaseAddress = new Uri("https://localhost:8080") };
_ = Program.RunAppAsync(app);
}
I can even perform a successful HttpClient.GetAsync("https://localhost:8080") immediately before the ClientWebSocket fails
System.Net.WebSockets.WebSocketException : The server returned status code '404' when status code '101' was expected.
Does anyone have any ideas why this might be?
Set ApplicationName in the WebApplicationOptions sent to WebApplication.CreateBuilder
WebApplication.CreateBuilder
(
new WebApplicationOptions
{
ApplicationName = typeof(Gambit.GameServer.Program).Assembly.GetName().Name // <==
}
);
Now it will be able to find your manifest file when running from a test.
See the following blog post for more of the back story on how I figured it out.
https://thefreezeteam.com/posts/StevenTCramer/2022/08/25/runwebserverintest

Azure Webjob, KeyVault Configuration extension, Socket Error

Need some help to determine if this is a bug in my code or in the config kevault extensions.
I have a netcore console based webjob. all working fine until a few weeks ago when we stated getting occasional startup errors which were Socket Error 10060 - Socket timed out or "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond"
These were all related to loading configuration layers (app settings, env, command line and keyvault). The errors stemmed from the keyvault once the build was executed on the hostbuilder.
I initially added the retry policy with the default HttpStatusCodeErrorDetectionStrategy and an exponential back-off but this is not executing.
finally I added my own retry policy with my own detection strategy (see below). Still not being fired.
I have stripped down the code to a hello world like example and included the messages from the webjob.
Here is the code summary:
Main
public static async Task<int> Main(string[] args)
{
var host = CreateHostBuilder(args)
.UseConsoleLifetime()
.Build();
using var serviceScope = host.Services.CreateScope();
var services = serviceScope.ServiceProvider;
//**stripped down to logging just for debug
var loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Main");
logger.LogDebug("Hello Test App Started OK. Exiting.");
//**Normally lots of service calls go here to do real work**
return 0;
}
HostBuilder - why hostbuilder? We use lots of components that are built for webapi and webapps so it was convenient to use a similar services model.
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((ctx, config) =>
{
//override with keyvault
var azureServiceTokenProvider = new AzureServiceTokenProvider(); //this is awesome - it will use MSI or Visual Studio connection
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var retryPolicy = new RetryPolicy<ServerErrorDetectionStrategy>(
new ExponentialBackoffRetryStrategy(
retryCount: 5,
minBackoff: TimeSpan.FromSeconds(1.0),
maxBackoff: TimeSpan.FromSeconds(16.0),
deltaBackoff: TimeSpan.FromSeconds(2.0)
)
);
retryPolicy.Retrying += RetryPolicy_Retrying;
keyVaultClient.SetRetryPolicy(retryPolicy);
var prebuiltConfig = config.Build();
config.AddAzureKeyVault(prebuiltConfig.GetSection("KeyVaultSettings").GetValue<string>("KeyVaultUri"), keyVaultClient, new DefaultKeyVaultSecretManager());
config.AddCommandLine(args);
})
.ConfigureLogging((ctx, loggingBuilder) => //note - this is run AFTER app configuration - whatever the order it is in.
{
loggingBuilder.ClearProviders();
loggingBuilder
.AddConsole()
.AddDebug()
.AddApplicationInsightsWebJobs(config => config.InstrumentationKey = ctx.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"]);
})
.ConfigureServices((ctx, services) =>
{
services
.AddApplicationInsightsTelemetry();
services
.AddOptions();
});
return host;
}
Event - this is never fired.
private static void RetryPolicy_Retrying(object sender, RetryingEventArgs e)
{
Console.WriteLine($"Retrying, count = {e.CurrentRetryCount}, Last Exception={e.LastException}, Delay={e.Delay}");
}
Retry Policy - only fires for the non-MSI attempt to contact the keyvault.
public class ServerErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
if (ex != null)
{
Console.WriteLine($"Exception {ex.Message} received, {ex.GetType()?.FullName}");
HttpRequestWithStatusException httpException;
if ((httpException = ex as HttpRequestWithStatusException) != null)
{
switch(httpException.StatusCode)
{
case HttpStatusCode.RequestTimeout:
case HttpStatusCode.GatewayTimeout:
case HttpStatusCode.InternalServerError:
case HttpStatusCode.ServiceUnavailable:
return true;
}
}
SocketException socketException;
if((socketException = (ex as SocketException)) != null)
{
Console.WriteLine($"Exception {socketException.Message} received, Error Code: {socketException.ErrorCode}, SocketErrorCode: {socketException.SocketErrorCode}");
if (socketException.SocketErrorCode == SocketError.TimedOut)
{
return true;
}
}
}
return false;
}
}
WebJob Output
[SYS INFO] Status changed to Initializing
[SYS INFO] Run script 'run.cmd' with script host - 'WindowsScriptHost'
[SYS INFO] Status changed to Running
[INFO]
[INFO] D:\local\Temp\jobs\triggered\HelloWebJob\42wj5ipx.ukj>dotnet HelloWebJob.dll
[INFO] Exception Response status code indicates server error: 401 (Unauthorized). received, Microsoft.Rest.TransientFaultHandling.HttpRequestWithStatusException
[INFO] Exception A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. received, System.Net.Http.HttpRequestException
[ERR ] Unhandled exception. System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
[ERR ] ---> System.Net.Sockets.SocketException (10060): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
[ERR ] at System.Net.Http.ConnectHelper.ConnectAsync(String host, Int32 port, CancellationToken cancellationToken)
[ERR ] --- End of inner exception stack trace ---
[ERR ] at Microsoft.Rest.RetryDelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
[ERR ] at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
[ERR ] at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretWithHttpMessagesAsync(String vaultBaseUrl, String secretName, String secretVersion, Dictionary`2 customHeaders, CancellationToken cancellationToken)
[ERR ] at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretAsync(IKeyVaultClient operations, String secretIdentifier, CancellationToken cancellationToken)
[ERR ] at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
[ERR ] at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
[ERR ] at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
[ERR ] at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
[ERR ] at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
[ERR ] at Microsoft.Extensions.Hosting.HostBuilder.Build()
[ERR ] at HelloWebJob.Program.Main(String[] args) in C:\Users\mark\Source\Repos\HelloWebJob\HelloWebJob\Program.cs:line 21
[ERR ] at HelloWebJob.Program.<Main>(String[] args)
[SYS INFO] Status changed to Failed
[SYS ERR ] Job failed due to exit code -532462766
This is an issue in the KV connectivity which is identified by the PG. Below is an official statement from Product Group:
The Microsoft Azure App Service Team has identified an issue with the
Key Vault references for App Service and Azure Functions feature
related to intermittent failure to resolve references at runtime.
Engineers identified a regression in the system that reduced the
performance and availability of our scale unit’s ability to retrieve
key vault references at runtime. A patch has been written and deployed
to our fleet of VMs to mitigate this issue.
We are continuously taking steps to improve the Azure Web App service
and our processes to ensure such incidents do not occur in the future,
and in this case, it includes (but is not limited to): Improving
detection and testing of performance and availability of the Key Vault
App Setting References feature Improvements to our platform to ensure
high availability of this feature at runtime. We apologize for any
inconvenience.
For almost everyone, updating packages to the new Microsoft.Azure packages has mitigated this issue, so trying those would be my first suggestion.
Thanks #HarshitaSingh-MSFT, makes sense though I searched for this when I had the problem and couldn't find it.
As a work around, I added some basic retry code to the startup.
Main looks like this for now:
public static async Task<int> Main(string[] args)
{
IHost host = null;
int retries = 5;
while (true)
{
try
{
Console.WriteLine("Building Host...");
var hostBuilder = CreateHostBuilder(args)
.UseConsoleLifetime();
host = hostBuilder.Build();
break;
}
catch (HttpRequestException hEx)
{
Console.WriteLine($"HTTP Exception in host builder. {hEx.Message}, Name:{hEx.GetType().Name}");
SocketException se;
if ((se = hEx.InnerException as SocketException) != null)
{
if (se.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine($"Socket error in host builder. Retrying...");
if (retries > 0)
{
retries--;
await Task.Delay(5000);
host?.Dispose();
}
else
{
throw;
}
}
else
{
throw;
}
}
}
}
using var serviceScope = host.Services.CreateScope();
var services = serviceScope.ServiceProvider;
var transferService = services.GetRequiredService<IRunPinTransfer>();
var result = await transferService.ProcessAsync();
return result;
}

Why won't my Raspberry Pi connect to Google Cloud IoT?

I've added my rsa_private.pem in a certs directory of my project and added the corresponding rsa_cert.pem public key to my device in the IOT Core console.
I also ran wget https://pki.google.com/roots.pem from within the certs directory.
Something I don't understand is that the roots.pem file that got generated now has multiple -
----BEGIN CERTIFICATE-----
// RS256_X509 encoded cert here
-----END CERTIFICATE-----
with various Operating CA:s; Comodo Group, GoDaddy, DigiCert, Entrust Datacard, GlobalSign and Google Trust Services LLC which is what the original root.pem had when I first generated it.
I've tried adding the root.pem to my-registry in the console under CA CERTIFICATES but get an error that says Certificate value is too big
`
When I run node gcloud.js I either get connecting... followed by multiple close pings in the terminal if I include protocolId: 'MQIsdp' in my connectionArgs that gets passed tomqtt.connect() or I get Error: Connection refused: Not authorized if I leave it out.
gcloud.js
const fs = require('fs')
const jwt = require('jsonwebtoken')
const mqtt = require('mqtt')
const exec = require('child_process').exec
const projectId = 'my-project'
const cloudRegion = 'us-central1'
const registryId = 'my-registry'
const mqttHost = 'mqtt.googleapis.com'
const mqttPort = 8883
const privateKeyFile = './certs/rsa_private.pem'
const algorithm = 'RS256'
var messageType = 'events'// or event
function createJwt (projectId, privateKeyFile, algorithm) {
var token = {
iat: parseInt(Date.now() / 1000),
exp: parseInt(Date.now() / 1000) + 86400 * 60, // 1 day
aud: projectId
}
const privateKey = fs.readFileSync(privateKeyFile)
return jwt.sign(token, privateKey, {
algorithm: algorithm
})
}
function fetchData () {}
async function configureDevice () {
// Get the device's serial number
await exec('cat /proc/cpuinfo | grep Serial', (error, stdout, stderr) => {
if (error) {
console.error(`exec error: ${error}`)
return
}
// Concat serial number w/ project name to meet device id naming requirement starting w/ a letter
const deviceId = `my-project${stdout.split(' ')[1].split('\n')[0]}`
var mqttClientId = 'projects/' + projectId + '/locations/' + cloudRegion + '/registries/' + registryId + '/devices/' + deviceId
var mqttTopic = '/devices/' + deviceId + '/' + messageType
var connectionArgs = {
// protocolId: 'MQIsdp' if I add this property I see "connecting..." get logged in the console
// followed by "close" repeatedly being logged. If I don't add it I get
// "Error: Connection refused: Not authorized"
host: mqttHost,
port: mqttPort,
clientId: mqttClientId,
username: 'unused',
password: createJwt(projectId, privateKeyFile, algorithm),
protocol: 'mqtts',
secureProtocol: 'TLSv1_2_method'
}
console.log('connecting...')
var client = mqtt.connect(connectionArgs)
// Subscribe to the /devices/{device-id}/config topic to receive config updates.
client.subscribe('/devices/' + deviceId + '/config')
client.on('connect', function (success) {
if (success) {
console.log('Client connected...')
sendData()
} else {
console.log('Client not connected...')
}
})
client.on('close', function () {
debugger
console.log('close')
})
client.on('error', function (err) {
debugger
console.log('error', err)
})
client.on('message', function (topic, message, packet) {
console.log(topic, 'message received: ', Buffer.from(message, 'base64').toString('ascii'))
})
function sendData () {
var payload = fetchData()
payload = JSON.stringify(payload)
console.log(mqttTopic, ': Publishing message:', payload)
client.publish(mqttTopic, payload, { qos: 1 })
console.log('Transmitting in 30 seconds')
setTimeout(sendData, 30000)
}
})
}
configureDevice()
So first, just a note on the roots.pem. There's multiple certs in there because the roots.pem is covering itself for if/when Google rotates the authorizing certificate. So it includes a bunch to allow for the roots.pem to be valid for longer, that's all. If you can identify which is the active cert (it changes maybe every three or four months ish?) you can actually delete all the other certs and just leave the one active cert. I had to do that for awhile when I was working on a seriously constrained MC with only 200k space on it. The roots.pem was too big, took up most of my storage on the device. There's more info here: https://cloud.google.com/iot/docs/how-tos/mqtt-bridge#using_a_long-term_mqtt_domain about a long-term domain that we setup to enable using a smaller root cert against a long term domain.
And you don't want/need to add the roots.pem into the registry-level certificate, as that's only needed if you want to use your own certificate authority to validate against new devices being registered. It's a bit confusing, but it doesn't have anything to do with authorizing devices after they're registered, it's about guarding against someone hacking your project and registering their own devices.
As for why the code isn't working: Your JWT is invalid because your expiration tag is: exp: parseInt(Date.now() / 1000) + 86400 * 60, // 1 day which is longer than JWTs are valid for. 86400 seconds * 60 is 1,440 hours... JWT's longer than 24 hours are rejected. So that's a bad error message. I mean, it's TECHNICALLY correct because the password is the JWT, and that's invalid, so it's an invalid pw...but it could probably be better. I'll take back to the team, but try changing that 86400 * 60 to just 86400 and it should work.
Per comment below: It was also the device name. Since it was dynamically generated, there was a missed character (dash instead of underscore).
Old Answer:
As for the rest, I don't see anything blaringly obvious, but what happens if you remove the async/await wrapper, and run it synchronously? Also just verify that your SSL key was created and registered with the same protocol (RSA, with or without the X509 wrapper). I've had issues before with registering one way and have the key actually be the other way.

How to solve the rippled NotConnectedError?

I installed rippled and started the service by using sudo systemctl start rippled.
When checking the status of rippled, it is working.
But I can't connect to the server in node.js.
I tried like below.
var RippleAPI = require('ripple-lib').RippleAPI;
var rippleAPI= new RippleAPI({
server: 'wss://localhost:5005'
});
rippleAPI.on('error', (errorCode, errorMessage) => {
console.log(errorCode + ': ' + errorMessage);
});
rippleAPI.on('connected', () => {
console.log('connected');
});
rippleAPI.on('disconnected', (code) => {
// code - [close code](https://developer.mozilla.org/en-
US/docs/Web/API/CloseEvent) sent by the server
// will be 1000 if this was normal closure
console.log('disconnected, code:', code);
});
rippleAPI.connect().then(() => {
}).then(() => {
return rippleAPI.disconnect();
}).catch(console.error);
When I start the node, it says NotConnectedError(socket hang up).
How can I connect?
Check if you've enabled wss in your rippled.cfg. It is commented out by default.
Otherwise rippled sometimes looks like it's running even when the server_state indicates full. And when you try ledger current it returns "error_code":17 InsufficientNetworkMode. In this case all you can do is wait.
Maybe your server doesn't have the enough resources. Check out their documentation. They've recently updated it and it's even more easier to setup a rippled server now.

How to call processing page via web service

I have a processing page and I want to run function process all via web service (add web reference into my C# window form app). My code below:
var context = new ModuleABCService.Screen() // limk web services: http://localhost:8686/soap/DMSBL009.asmx
{
CookieContainer = new CookieContainer(),
AllowAutoRedirect = true,
EnableDecompression = true,
Timeout = 60000
};
var loginResult = context.Login(string.Format("{0}#{1}", val.UserName, company), val.Password);
if (loginResult.Code != ErrorCode.OK)
{
throw new Exception(string.Format("Can not login {0}", company));
}
Content content = context.GetSchema();
context.Clear();
context.Submit(
new Command[]
{
content.Actions.ProcessAll
}
);
And I got an exception message:
System.Web.Services.Protocols.SoapExceptio:n Server was unable to process request. ---> PX.Data.PXUndefinedCompanyException: Unable determine proper company id for the request. at PX.Data.PXDatabaseProviderBase.getCompanyID(String tableName, companySetting& setting) in c:\Builders\4_10-2014_4_28-21_21_17-Full\Scripts\BuildTemp\NetTools\PX.Data\Database\Common\DbProviderBaseCompanies.cs:line 471...
Have you ever got this error before? Could you please give me any suggestion? Thank you so much!
Ok, I found out, because Acumatica's license