i'm new to Ktor and i am currently using the quick start http api but i receive the error:
ERROR Application - Unhandled: GET - /snippets
com.fasterxml.jackson.databind.JsonMappingException: kotlin.reflect.jvm.internal.KClassImpl cannot be cast to kotlin.reflect.jvm.internal.KClassImpl (through reference chain: java.util.Collections$Singleton
Map["snippets"]->java.util.ArrayList[0])
CODE:
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.application.*
import io.ktor.features.CallLogging
import io.ktor.features.ContentNegotiation
import io.ktor.features.DefaultHeaders
import io.ktor.jackson.jackson
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.Routing
import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.routing
import java.util.*
data class Snippet(val text: String)
val snippets = Collections.synchronizedList(mutableListOf(
Snippet("hello"),
Snippet("world")
))
fun Application.main() {
install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
routing {
get("/snippets") {
call.respond(mapOf("snippets" to synchronized(snippets) { snippets.toList() }))
}
}
}
If i use this instead:
call.respond(mapOf("snippets" to synchronized(snippets) { snippets.toString() }))
it returns:
{
"snippets" : "[Snippet(text=hello), Snippet(text=world)]"
}
but now it's using toString() rather than toList(), any idea how i can get it to work as in the quick start using toList()?
Found the issue.
using the watch option in the application.conf file to run the server seems to mess a couple things up.
application.conf file:
ktor {
deployment {
port = 8080
watch = [ / ]
}
application {
modules = [ com.MainKt.main ]
}
}
removing
watch = [ / ]
or switching back to the embedded server seems to have fixed the issue.
fun main() {
embeddedServer(Netty, 8080) {
//rest of the code
}.start(wait = true)
}
Related
I have experience on Jquery but Angular is very new to me and so is spartacus. I have been trying to configure our Spartacus to be used across multiple environments. The only solution that I could find was to use the meta tag on index.html and then grab the config based on url as mentioned by janwidmer and CarlEricLavoie. I did make a slight change from these 2 solutions though, and that was to use the environment.{env}.ts files instead of a new Backend service or keeping the config in a map in the same file.
The issue that I am facing is that the spartacus.cofniguration.module.ts file needs these values in #NgModule and I've tried everything that I could think of, but am unable to use the value of this.config.backend?.occ?.baseUrl inside the NgModule, where I need to give the value for base URL and Base site.
Below is what I am trying to do. i have written the logic that would expose the correct config object according to the OCC base url that I receive on the environment. Now over here, the console log inside the constructor prints a proper object with all the properties I need, but then the conf object right before the #NgModule, it prints undefined and the values inside the NgModule are undefined as well then.
I also tried to create a new class and import it here so I could maybe create an instance, but couldn't do that as well, as it would then need the variable/method to be static for me to be able to access it inside NgModule.
import { Injectable, NgModule } from '#angular/core';
import { FacetChangedEvent, FeaturesConfig, I18nConfig, OccConfig, provideConfig, SiteContextConfig } from "#spartacus/core";
import { environment } from '../../environments/environment';
import { defaultB2bCheckoutConfig, defaultB2bOccConfig } from "#spartacus/setup";
import { defaultCmsContentProviders, layoutConfig, mediaConfig } from "#spartacus/storefront";
import { translations } from 'src/assets/i18n-translations/translations';
import { translationChunksConfig } from 'src/assets/i18n-translations/translation-chunks-config';
import { CdcConfig, CdcRootModule, CDC_FEATURE } from '#spartacus/cdc/root';
import { EnvironmentConfigurationModule } from "../../environments/environment-configuration.module"
import { environment as envdev} from '../../environments/environment';
import { environment as envstag} from '../../environments/environment.stage';
import { environment as envprod} from '../../environments/environment.prod';
let conf : any;
console.log("before ng");
console.log(conf);
#NgModule({
declarations: [],
imports: [
],
providers: [provideConfig(layoutConfig), provideConfig(mediaConfig), ...defaultCmsContentProviders, provideConfig(<OccConfig><unknown>{
backend: {
occ: {
baseUrl: environment.baseUrl,
}
},
}), provideConfig(<SiteContextConfig>{
context: {
urlParameters: ['baseSite', 'language', 'currency'],
baseSite: [environment?.baseSite],
currency: ['USD', 'GBP',]
},
}),
provideConfig(<I18nConfig>{
i18n: {
backend:{
loadPath:'assets/i18n-translations/{{lng}}/{{ns}}.ts',
},
resources: translations,
chunks: translationChunksConfig
,fallbackLang: 'en'
},
}
), provideConfig(<FeaturesConfig>{
features: {
level: '4.2'
}
}),
provideConfig(defaultB2bOccConfig), provideConfig(defaultB2bCheckoutConfig),
]
})
export class SpartacusConfigurationModule {
urlValue : string | undefined;
env: any;
constructor(private config: OccConfig) {
this.urlValue = this.config.backend?.occ?.baseUrl;
console.log("baseurl : " + this.config.backend?.occ?.baseUrl);
if(this.urlValue?.includes('s1'))
{
this.env=envstag;
}
else if(this.urlValue?.includes('p1'))
{
this.env=envprod;
}
else{
this.env=envdev;
}
conf = this.env;
console.log("conf");
console.log(conf);
}
getConfig() {
return conf;
}
}
Apart from these solutions, we have also tried to use window.location and location.href to find the base url and work based on that. This works amazing on local, but as soon as you deploy it to the server, it says that the reference window not found/reference location not found. We tried to do this right before the NgModule, inside spartacus-configuration.module.ts
import { environment as envDev } from "../../environments/environment";
import { environment as envStage } from "../../environments/environment.stage";
import { environment as envProd } from "../../environments/environment.prod";
let loc=location.hostname;
let env;
if(loc.includes('s1'))
{
env=envStage;
}
else if(loc.includes('p1'))
{
env=envProd;
}
else{
env=envDev;
}
console.log("before ng===>>>",loc);
With environment imports, your standard build configuration will replace the environment.ts variables with the ones set by your server's environment (eg. process.env) before deployment. Therefore, you should only import from environment.ts in your code and let the server handle overriding the staging environment variables.
With location and window objects, they are not accessible on the server-side of the build because Angular Universal initially delivers a pre-rendered page readable by bots (usually html-only) for SEO purposes. The location and window objects do not exist in this instance. In Angular, window and location objects should be imported into your classes.
Use Window: https://stackoverflow.com/a/52620181/12566149
Use Location: https://stackoverflow.com/a/43093554/12566149
I'd like to add a shutdown route to my Ktor server but I need it to require authentication.
I'm trying to put the shutdown url in my authenticated routes like so:
// application.conf
ktor {
deployment {
port = 8080
host = 127.0.0.1
shutdown.url = "/shutdown"
}
}
// Application.kt
routing {
root()
authenticate("authenticated-routes") {
test1()
test2()
shutdown()
}
}
// Routes.kt
fun Route.shutdown() {
get("/shutdown") {
// shutting down
}
}
But somehow the shutdown route does not require authentication for shutting down the server (something to do with the config overriding the route defined in Routes.kt?)
The docs unfortunately do not give any hints as to how to make the shutdown route authenticated. Any ideas on how I could make sure not just anyone can call the shutdown route and shutdown my server?
The ShutDownUrl plugin has nothing with Routing that's why you can't integrate it with the Authentication plugin. To solve your problem you can manually make an instance of ShutDownUrl class and execute the doShutdown method in a route that may require authentication. Here is an example:
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
fun main() {
val shutdown = ShutDownUrl("") { 1 }
embeddedServer(Netty, port = 3333) {
install(Authentication) {
basic {
realm = "Access for shutting the server down"
validate { credentials ->
if (credentials.name == "jetbrains" && credentials.password == "foobar") {
UserIdPrincipal(credentials.name)
} else {
null
}
}
}
}
routing {
get("/") {
call.respondText { "hello" }
}
authenticate {
get("/shutdown") {
shutdown.doShutdown(call)
}
}
}
}.start()
}
I have a simple nestjs application, where I have set up a CacheModule using Redis store as follows:
import * as redisStore from 'cache-manager-redis-store';
CacheModule.register({
store: redisStore,
host: 'redis',
port: 6379,
}),
I would like to use it to store a single value, however, I do not want to do it the built-in way by attaching an interceptor to a controller method, but instead I want to control it manually and be able to set and retrieve the value in the code.
How would I go about doing that and would I even use cache manager for that?
You can use the official way from Nest.js:
1. Create your RedisCacheModule:
1.1. redisCache.module.ts:
import { Module, CacheModule } from '#nestjs/common';
import { ConfigModule, ConfigService } from '#nestjs/config';
import * as redisStore from 'cache-manager-redis-store';
import { RedisCacheService } from './redisCache.service';
#Module({
imports: [
CacheModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
store: redisStore,
host: configService.get('REDIS_HOST'),
port: configService.get('REDIS_PORT'),
ttl: configService.get('CACHE_TTL'),
}),
}),
],
providers: [RedisCacheService],
exports: [RedisCacheService] // This is IMPORTANT, you need to export RedisCacheService here so that other modules can use it
})
export class RedisCacheModule {}
1.2. redisCache.service.ts:
import { Injectable, Inject, CACHE_MANAGER } from '#nestjs/common';
import { Cache } from 'cache-manager';
#Injectable()
export class RedisCacheService {
constructor(
#Inject(CACHE_MANAGER) private readonly cache: Cache,
) {}
async get(key) {
await this.cache.get(key);
}
async set(key, value) {
await this.cache.set(key, value);
}
}
2. Inject RedisCacheModule wherever you need it:
Let's just assume we will use it in module DailyReportModule:
2.1. dailyReport.module.ts:
import { Module } from '#nestjs/common';
import { RedisCacheModule } from '../cache/redisCache.module';
import { DailyReportService } from './dailyReport.service';
#Module({
imports: [RedisCacheModule],
providers: [DailyReportService],
})
export class DailyReportModule {}
2.2. dailyReport.service.ts
We will use the redisCacheService here:
import { Injectable, Logger } from '#nestjs/common';
import { Cron } from '#nestjs/schedule';
import { RedisCacheService } from '../cache/redisCache.service';
#Injectable()
export class DailyReportService {
private readonly logger = new Logger(DailyReportService.name);
constructor(
private readonly redisCacheService: RedisCacheService, // REMEMBER TO INJECT THIS
) {}
#Cron('0 1 0 * * *') // Run cron job at 00:01:00 everyday
async handleCacheDailyReport() {
this.logger.debug('Handle cache to Redis');
}
}
You can check my sample code here.
Building on Ahmad's comment above, I used the following to enable redis in my nestjs application:
Install and setup nestjs-redis https://www.npmjs.com/package/nestjs-redis per docs.
See the docs here on how to write and read values in a Redis store:
https://github.com/NodeRedis/node-redis
If you're connection a external Redis, I recommend to use 'async-redis' package.
The code will be:
import * as redis from 'async-redis';
import redisConfig from '../../config/redis';
On redisConfig:
export default {
host: 'your Host',
port: parseInt('Your Port Conection'),
// Put the first value in hours
// Time to expire a data on redis
expire: 1 * 60 * 60,
auth_pass: 'password',
};
So, you run:
var dbConnection = redis.createClient(config.db.port, config.db.host,
{no_ready_check: true});
Now you can, execute commands like set and get for your Redis Database.
I am trying to use inversify JS to inject dependencies on TypeScript App. I started by using the example of https://github.com/inversify/InversifyJS page :
// file interfaces.ts
interface Warrior {
fight(): string;
}
// file types.ts
const TYPES = {
Warrior: Symbol("Warrior")
};
export { TYPES };
// file entities.ts
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { Warrior } from "./interfaces"
import { TYPES } from "./types";
#injectable()
class WarriorImpl implements Warrior {
public constructor(){
}
public fight() { return "I fight"; }
}
export { WarriorImpl };
// file inversify.config.ts
import { Container } from "inversify";
import TYPES from "./types";
import { Warrior } from "./interfaces";
import { WarriorImpl } from "./entities";
const myContainer = new Container();
myContainer.bind<Warrior>(TYPES.Warrior).to(WarriorImpl);
export { myContainer };
I applied what is provided in the example but Vscode and tsc failed at the binding line by showing this error [ts] Untyped function calls may not accept type arguments. [ts] Cannot find name 'Warrior'.
Your example is clean and correct all you have to do is to upgrade your typescript version in your package.json
"devDependencies": {
"typescript": "3.9.3"
}
You have to export the interface for it to be imported:
// file interfaces.ts
export interface Warrior {
fight(): string;
}
Your import in inversify.config.ts should be:
import { TYPES } from "./types";
Make sure you have actually installed the library (npm install inversify). The error Untyped function calls may not accept type arguments is sue to missing type information.
I am invoking a procedure on mobilefirst 8.0 adapter from ionic 2
I am able to see the response in device but not in chrome.Got the below error
ReferenceError: WLResourceRequest is not defined.Below is my code
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { TransitPage } from '../transit/transit';
import { TrackDartPage } from '../track-dart/track-dart';
import { LocationFinderPage } from '../location-finder/location-finder';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
declare var WLResourceRequest;
calladapter(){
var resourceRequest = new WLResourceRequest("/adapters/trackDart/status/" + 2,WLResourceRequest.GET);
resourceRequest.send().then((response) => {
alert(JSON.stringify(response.responseText));
},
function(error){
alert(JSON.stringify(error));
});
}
By the error I suspect that you are running ionic serve which is leading you to this error since ionic does not know about MobileFirst. Instead, run mfpdev app preview after running npm run watch command.