unable to create `Environment` in `Silhouette` - silhouette

I am trying to create the Environment in Silhouette but am unable to. I have defined the Identity and Authenticator as follows
trait SessionEnv extends Env {
type I = User
type A = SessionAuthenticator
}
Next, I suppose I have to create the Environment. For that I have written the following code but am stick as I do not understand how to pass the different parameters expected by Environment's apply method
Environment companion object's apply method has signature
def apply[E <: Env](
identityServiceImpl: IdentityService[E#I],
authenticatorServiceImpl: AuthenticatorService[E#A],
requestProvidersImpl: Seq[RequestProvider],
eventBusImpl: EventBus
I know that I have to provide the implemention of IdentityService. I have done so as follows
class UserService #Inject()(userDao:UsersRepository) extends IdentityService[User] {...}
User is defined as follows
case class UserProfile(
loginInfo:LoginInfo,
confirmed: Boolean,
email:Option[String],
firstName: Option[String],
lastName: Option[String],
passwordInfo:Option[PasswordInfo]
//oauth1Info: Option[OAuth1Info],
//avatarUrl: Option[String]) {
)
//representation of a user. A user has an Id and a profile
case class User (id:UUID, profile:UserProfile)
But what do I pass for other values required by apply - authenticatorServiceImpl: AuthenticatorService[E#A],
requestProvidersImpl: Seq[RequestProvider],
eventBusImpl: EventBus
val sessionEnv = com.mohiva.play.silhouette.api.Environment[SessionEnv](new UserService(userRepository),????)
Also, I suppose I don't have to use Guice as I am using compile time injection. Is that correct?
UPDATE
I changed from SessionAuthenticatorService to CookieAuthenticatorService to try some code available online.
It seems my understanding that Silhouette provides some default implementations isn't exactly correct. I thought that I could simply use the SessionAuthenticatorService companion object defined in https://github.com/mohiva/play-silhouette/blob/master/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/SessionAuthenticator.scala but that is not the case. Looking at some code created in ScalaModule, it seems that I'll have to create the required object myself but I'll need to do it in my AppLoader class (for compile time DI) instead of ScalaModule (for runtime DI). However, I still have not solved the problem. I do not know how to create the signer required by CookieAuthenticatorService
val config = configuration.underlying.asInstanceOf[CookieAuthenticatorSettings]("silhouette.authenticator")
val fingerprintGenerator = new DefaultFingerprintGenerator(false)
val idGenerator = new SecureRandomIDGenerator()
val clock:Clock = Clock()
val authenticatorService: AuthenticatorService[CookieAuthenticator] = new CookieAuthenticatorService(config,None,,,fingerprintGenerator, idGenerator,clock) //STILL NEED TO FIND OUT HOW TO CREATE Signer AND CookieHeaderEncoding required by CookieAuthenticator service
val cookieEnv = com.mohiva.play.silhouette.api.Environment[CookieEnv](userIdentityService ,authenticatorService,Seq(),EventBus())

Here is the implementation for cookie authenticator
import com.mohiva.play.silhouette.api.actions._
import com.mohiva.play.silhouette.api.{EventBus, SilhouetteProvider}
import com.mohiva.play.silhouette.api.crypto.CrypterAuthenticatorEncoder
import com.mohiva.play.silhouette.api.util.Clock
import com.mohiva.play.silhouette.crypto.{JcaCrypter, JcaCrypterSettings, JcaSigner, JcaSignerSettings}
import com.mohiva.play.silhouette.impl.authenticators.{CookieAuthenticatorService, CookieAuthenticatorSettings}
import com.mohiva.play.silhouette.impl.util.{DefaultFingerprintGenerator, SecureRandomIDGenerator}
import components._
import play.api.mvc.DefaultCookieHeaderEncoding
import controllers._
import play.api._
import services.db.cassandra.UserService
import play.filters.csrf._
import services.AtomicCounter
import play.api.ApplicationLoader.Context
import play.filters.HttpFiltersComponents
import router.Routes
class AppLoader extends ApplicationLoader {
override def load(context: ApplicationLoader.Context): Application = {
LoggerConfigurator(context.environment.classLoader).foreach {
_.configure(context.environment, context.initialConfiguration, Map.empty)
}
new AppComponents(context).application
}
}
class AppComponents (context: Context) extends BuiltInComponentsFromContext(context)
with CassandraRepositoryComponents
with HttpFiltersComponents
with AssetsComponents
with CSRFComponents
/*with SilhouetteComponents*/{ //TODOM - Would prefer SilhouetteComponent but creating an Environment requires IdentityService. UserService is an IdentifyService but it requires userRepository which is created here. Need to resolve this cross dependence
val userIdentityService = new UserService(userRepository) //responsible for retrieving user information (eg email id) from a database
val config = CookieAuthenticatorSettings()
val fingerprintGenerator = new DefaultFingerprintGenerator(false)
val idGenerator = new SecureRandomIDGenerator()
val clock:Clock = Clock()
val signer= new JcaSigner(new JcaSignerSettings("someSigner"))
val crypter = new JcaCrypter(new JcaCrypterSettings("someCrypter"))
val authenticatorEncoder = new CrypterAuthenticatorEncoder(crypter)
val cookieHeaderEncoding= new DefaultCookieHeaderEncoding()
val authenticatorService = new CookieAuthenticatorService(config, None, signer, cookieHeaderEncoding, authenticatorEncoder, fingerprintGenerator, idGenerator, clock)
val cookieEnv = com.mohiva.play.silhouette.api.Environment[CookieEnv](userIdentityService ,authenticatorService,Seq(),EventBus())
val defaultParser = new mvc.BodyParsers.Default()
val securedAction = new DefaultSecuredAction(new DefaultSecuredRequestHandler(new DefaultSecuredErrorHandler(messagesApi)), defaultParser )
val unsecuredAction = new DefaultUnsecuredAction(new DefaultUnsecuredRequestHandler(new DefaultUnsecuredErrorHandler(messagesApi)),defaultParser)
val userAware = new DefaultUserAwareAction(new DefaultUserAwareRequestHandler(),defaultParser)
val silhouette = new SilhouetteProvider[CookieEnv](cookieEnv,securedAction,unsecuredAction,userAware)
lazy val userRepositoryController = new UserController(userRepository, controllerComponents)
lazy val homeController = new HomeController(controllerComponents, csrfAddToken,csrfCheck,silhouette) //using Silhouette in only one controller for the moment
lazy val countController = new CountController(controllerComponents,new AtomicCounter())
lazy val asyncController = new AsyncController(controllerComponents, actorSystem)
lazy val userWSRoutes = new WSRouters.User.UserRouter(userRepositoryController) //TODOM - whatam i doing here?
lazy val router = new Routes(httpErrorHandler, homeController,userWSRoutes, countController,asyncController, assets)
}

Related

Gatling - requirement failed: No scenario set up

Im fairly new into programming, i was trying to launch my test but i got : Exception in thread "main" java.lang.IllegalArgumentException: requirement failed: No scenario set up
even if i got the scenario set up (which is confusing)
i'm pretty sure is something obvious that newbie like me can't figure out unfortunately.
i tried invalidating caches, and reloading the project.
im using intelij + maven
package simulations
import io.gatling.core.Predef._
import io.gatling.core.structure.{ChainBuilder, ScenarioBuilder}
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder
class GatlingDemoStore extends Simulation {
val domain = "demostore.gatling.io"
val httpProtocol: HttpProtocolBuilder = http
.baseUrl("http://" + domain)
object login {
def userlogin: ChainBuilder = {
exec(http("Login User")
.post("/login")
.formParam("_csrf", "${csrfValue}")
.formParam("username", "user1")
.formParam("password", "pass"))
}
}
object CmsPages {
def homepage: ChainBuilder = {
exec(http("Load Home Page")
.get("/")
.check(status.is(200))
.check(regex("<title>Gatling Demo-Store</title>").exists)
.check(css("#_csrf", "content").saveAs("csrfValue")))
}
def aboutus: ChainBuilder = {
exec(http("Load Home Page")
.get("/about-us")
.check(status.is(200))
.check(substring("About Us")))
}
def categories: ChainBuilder = {
exec(http("Load Categories Page")
.get("/category/all")
.check(status.is(200)))
.pause(10)
}
def productpage: ChainBuilder = {
exec(http("Load Product Page")
.get("/product/black-and-red-glasses")
.check(status.is(200)))
.pause(15)
}
def addtocart: ChainBuilder = {
exec(http("Add Product to Cart")
.get("/cart/add/19"))
}
def viewcart: ChainBuilder = {
exec(http("View Cart")
.get("/cart/view"))
}
def checkout: ChainBuilder = {
exec(http("Checkout")
.get("/cart/checkout"))
}
val User: ScenarioBuilder = scenario("DemoStore Simulation")
.exec(CmsPages.homepage)
.pause(5)
.exec(CmsPages.aboutus)
.pause(5)
.exec(CmsPages.categories)
.pause(20)
.exec(CmsPages.productpage)
.pause(5)
.exec(CmsPages.addtocart)
.pause(2)
.exec(login.userlogin)
setUp(
User.inject(atOnceUsers(1))
).protocols(httpProtocol)
}
}
I've reformatted your code so your error is more obvious: you're calling setUp not in the body of the GatlingDemoStore class but in the body of the CmsPages object, which is never loaded (in Scala, objects are lazily loaded and here you never call it).
Move setUp in the body of the GatlingDemoStore class.
Important note: as you're new to programming, you should probably go with Java (supported since Gatling 3.7) instead of Scala.
thank you very much for your help, greatly appreciated.
seems like a rookie mistake..
thats a great idea actually, ima do some research on how i can do that.

How to trigger ProcessTimeTimer with Flink MiniCluster

I've got a Flink KeyedCoProcessFunction that registers Processing Time Timers in a larger Flink stream job, and I'm trying to create unit tests for the entire job using the Flink MiniCluster. But I can't get the onTimer() call back in the KeyedCoProcessFunction to trigger.
Has anyone gotten this to work? Did it require any special configuration?
Switching to Event Time works fine, so I'm wondering if this just doesn't work with the Flink MiniCluster or is there something wrong with my implementation.
I wrote a simple test in Scala to see if I could get this to work.
import org.apache.flink.api.common.typeinfo.TypeInformation
import org.apache.flink.runtime.testutils.MiniClusterResourceConfiguration
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.functions.source.{ParallelSourceFunction, SourceFunction}
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.test.streaming.runtime.util.TestListResultSink
import org.apache.flink.test.util.MiniClusterWithClientResource
import org.apache.flink.util.Collector
import org.scalatest.BeforeAndAfter
import org.scalatest.flatspec.AnyFlatSpec
import org.slf4j.LoggerFactory
class TimerTest extends AnyFlatSpec with BeforeAndAfter {
private val SlotsPerTaskMgr = 1
val flinkCluster = new MiniClusterWithClientResource(new MiniClusterResourceConfiguration.Builder()
.setNumberSlotsPerTaskManager(SlotsPerTaskMgr)
.setNumberTaskManagers(1)
.build)
before {
flinkCluster.before()
}
after {
flinkCluster.after()
}
"MiniCluster" should "trigger onTimer" in {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
implicit val longTypeInfo: TypeInformation[Long] = TypeInformation.of(classOf[Long])
val sink = new TestListResultSink[Long]
env.addSource(new MyLongSource(100))
.keyBy(v => v)
.process(new MyProccesor())
.addSink(sink)
env.execute()
println("Received " + sink.getResult.size() + " output records.")
}
}
class MyProccesor extends KeyedProcessFunction[Long, Long, Long] {
private val log = LoggerFactory.getLogger(this.getClass)
override def processElement(
value: Long,
ctx: KeyedProcessFunction[Long, Long, Long]#Context,
out: Collector[Long]): Unit = {
log.info("Received {} at {}", value, ctx.timerService().currentProcessingTime())
if (value % 10 == 0) {
log.info("Scheduling processing timer for {}", ctx.timerService().currentProcessingTime() + 10)
ctx.timerService().registerProcessingTimeTimer(ctx.timerService().currentProcessingTime() + 10)
}
}
override def onTimer(
timestamp: Long,
ctx: KeyedProcessFunction[Long, Long, Long]#OnTimerContext,
out: Collector[Long]): Unit = {
log.info("Received onTimer at {}", timestamp)
out.collect(timestamp)
}
}
class MyLongSource(n:Int) extends ParallelSourceFunction[Long] {
#volatile private var stop = false
override def run(ctx: SourceFunction.SourceContext[Long]): Unit = {
for(i <- 1 to n) {
if(stop) return;
println("Sending " + i)
ctx.collect(i)
}
Thread.sleep(1000)
}
override def cancel(): Unit = {
stop = true
}
}
I was finally able to get some consistent results by adding a Thread.sleep(1000) at the end of the source run() method. Seems like once the source exits, messages get processed and then everything is shut down even if there are pending timers.
When a Flink job shuts down, any pending processing time timers are simply ignored. They never fire.
For what it's worth, there's some ongoing discussion on the Flink dev mailing list about offering an option to trigger all pending processing time timers. See http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/DISCUSS-FLIP-134-DataStream-Semantics-for-Bounded-Input-td37365.html#a37558.

Receiving HttpError 403 When executing custom google endpoints api

I have created an endpoints api in Google Apps Engine with python. The api requires oAuth2 authentication. The method works perfectly from the api explorer, however I can't seem to figure out why I get an HttpError 403 "Client_id is not allowed" at runtime. I know that the client id is valid because I have been able to use it successfully in api explorer. I know I am exposing my client secret in this post, but I need the help. Once I get it working I will request a new secret and client id.
The code that is calling the API:
from apiclient.discovery import build
from oauth2client.appengine import OAuth2Decorator
import logging
import pprint
import webapp2
decorator = OAuth2Decorator(
client_id='968442935974-happ8rfg8eq059v1p8364bv5mj8tjd4l.apps.googleusercontent.com',
client_secret='q31TNSbsc70xfEwaTjXuVAPI',
scope='https://www.googleapis.com/auth/userinfo.email')
# For for production
api_root = 'https://jamesste-school-app.appspot.com/_ah/api'
api = 'knowledge'
version = 'v1'
discovery_url = '%s/discovery/v1/apis/%s/%s/rest' % (api_root, api, version)
class TestHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
service = build(api,version,discoveryServiceUrl=discovery_url)
self.response.headers['Content-Type'] = 'application/json'
knowledge_list = service.knowledge_message().list()
knowledge_list.execute(http=decorator.http())
self.response.out.write(pprint.pformat(knowledge_list))
app = webapp2.WSGIApplication([
('/test', TestHandler),
(decorator.callback_path, decorator.callback_handler())],
debug=True)
The API code:
from google.appengine.ext import ndb
from models import Knowledge
from models import KnowledgeType
from models import Resource
from protorpc import message_types
from protorpc import messages
from protorpc import remote
from apiuser import ApiUser
import endpoints
import logging
def get_endpoints_current_user(raise_unauthorized=True):
"""Returns a current user and (optionally) causes an HTTP 401 if no user.
Args:
raise_unauthorized: Boolean; defaults to True. If True, this method
raises an exception which causes an HTTP 401 Unauthorized to be
returned with the request.
Returns:
The signed in user if there is one, else None if there is no signed in
user and raise_unauthorized is False.
"""
current_user = endpoints.get_current_user()
if raise_unauthorized and current_user is None:
raise endpoints.UnauthorizedException('Invalid token.')
return current_user
class KnowledgeMessage(messages.Message):
knowledge_key = messages.StringField(1)
knowledge = messages.StringField(2)
resource_key = messages.StringField(3)
resource_name = messages.StringField(4)
resource_section_key = messages.StringField(5)
resource_section_name = messages.StringField(6)
types = messages.StringField(7, repeated=True)
page = messages.IntegerField(8)
keywords = messages.StringField(9, repeated=True)
user_email = messages.StringField(10)
user_org_key = messages.StringField(11)
class KnowledgeMessageList(messages.Message):
items = messages.MessageField(KnowledgeMessage, 1, repeated=True)
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
#endpoints.method(KnowledgeMessage, KnowledgeMessage, name="knowledge_message.insert", path="knowledge", http_method="POST")
def insert_knowledge(self, request):
authenticated_user = ApiUser()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.email = " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.get_org_key()")
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge = " + request.knowledge)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_key = " + request.resource_key)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_section_key = " + request.resource_section_key)
types_string = ""
for item in request.types:
types_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.types = " + types_string)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.page = " + str(request.page))
keywords_string = ""
for item in request.keywords:
keywords_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.keywords = " + keywords_string)
if request.knowledge_key:
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge_key = " + request.knowledge_key)
# If no key exists, then we can assume it needs to be inserted
# into the datastore. Overwrite any exiting values with what
# is passed into the method
kno=ndb.Key(urlsafe=request.knowledge_key).get()
kno.knowledge = request.knowledge
kno.resource = ndb.Key(urlsafe=request.resource_key)
kno.resource_section = ndb.Key(urlsafe=request.resource_section_key)
kno.types = request.types
kno.page = request.page
kno.keywords = request.keywords
kno.put()
else:
#get resource key and name
Knowledge(parent=PARENT_KEY,
knowledge=request.knowledge,
resource = ndb.Key(urlsafe=request.resource_key),
resource_section = ndb.Key(urlsafe=request.resource_section_key),
types = request.types,
page = request.page,
keywords = request.keywords
).put()
return request
#endpoints.method(message_types.VoidMessage, KnowledgeMessageList, name='knowledge_message.list', path='knowledge_messages', http_method='GET')
def list_knowledge(self, unused_request):
authenticated_user = ApiUser()
if not authenticated_user.is_authenticated:
logging.info("Logging: KnowledgeMessageApi.list_knowledge - user was not authenticated")
raise endpoints.ForbiddenException()
logging.info("finding current user info : " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
knos = []
for kno in Knowledge.query(ancestor=PARENT_KEY):
knos.append(KnowledgeMessage(knowledge_key=kno.key.urlsafe(),
knowledge=kno.knowledge,
resource_key=kno.resource.urlsafe(),
resource_name=kno.resource.get().name,
resource_section_key=kno.resource_section.urlsafe(),
resource_section_name=kno.resource_section.get().name,
types=kno.types,
page=kno.page,
keywords=kno.keywords,
user_email=authenticated_user.email,
user_org_key=PARENT_KEY.urlsafe())
)
return KnowledgeMessageList(items=knos)
app = endpoints.api_server([KnowledgeMessageApi])
I'll also include a few images of proofs that I explored prior to sending the question.
enter image description here
I will greatly appreciate any help I receive.
I discovered my mistake after much work:
In my api, I never specified which client_ids were permitted.
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
Apparently the code '1-web-apps.apps.googleusercontent.com' was a placeholder, and I never replaced it with my own. That has been corrected.

Encrypted database password in Play + Slick + HikariCP application

I'm using play-slick_2.11-1.0.1 + HikariCP 2.4.1 to access SqlServer in my Play4-based application.
The database connection in application.conf:
slick.dbs.myDatabase = {
driver="com.typesafe.slick.driver.ms.SQLServerDriver$"
db{
url = "jdbc:sqlserver://sqlserverhost"
driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
user = "admin"
password = "ENCRYPTED_PASSWORD"
}
}
The problem is that the database password configured here must be encrypted based on our company policy.
How can I inject my decryption code to decrypt the password for the connection?
Just found a solution:
def createDecryptedDbConfig (dbConfigProvider: DatabaseConfigProvider) : DatabaseConfig[JdbcProfile] = {
val dbConfig = dbConfigProvider.get[JdbcProfile]
val decryptedConfig = dbConfig.config.
withValue("db.user", ConfigValueFactory.fromAnyRef(decrypt(dbConfig.config.getConfig("db").getString("user")))).
withValue("db.password", ConfigValueFactory.fromAnyRef(decrypt(dbConfig.config.getConfig("db").getString("password"))))
DatabaseConfig.forConfig[JdbcProfile]("", decryptedConfig)
}
i was also trying with latest play 2.7 with slick 3.5
package models
import com.typesafe.config.ConfigValueFactory
import javax.inject.Inject
import play.api.Logger
import play.api.db.slick.DatabaseConfigProvider
import services.EncryptDecryptService
import slick.basic.{BasicProfile, DatabaseConfig}
import slick.jdbc.JdbcProfile
trait HasDatabaseConfigTalachitas[P <: BasicProfile] {
/** The Slick database configuration. */
var dbConfig: DatabaseConfig[P] // field is declared as a val because we want a stable identifier.
/** The Slick profile extracted from `dbConfig`. */
protected final lazy val profile: P = dbConfig.profile // field is lazy to avoid early initializer problems.
#deprecated("Use `profile` instead of `driver`", "2.1")
protected final lazy val driver: P = dbConfig.profile // field is lazy to avoid early initializer problems.
/** The Slick database extracted from `dbConfig`. */
protected final def db: P#Backend#Database = dbConfig.db
}
trait HasDatabaseConfigProviderTalachitas[P <: BasicProfile] extends HasDatabaseConfigTalachitas[P] {
/** The provider of a Slick `DatabaseConfig` instance.*/
protected val dbConfigProvider: DatabaseConfigProvider
override var dbConfig: DatabaseConfig[P] = dbConfigProvider.get[P] // field is lazy to avoid early initializer problems.
}
import play.api.Configuration
class CustomizedSlickConfig #Inject()(config: Configuration,
encryptDecryptService: EncryptDecryptService) {
val logger: Logger = Logger(this.getClass())
def createDbConfigCustomized (dbConfigProvider: DatabaseConfigProvider) : DatabaseConfig[JdbcProfile] = {
val user: String = encryptDecryptService.decrypt(config.get[String]("slick.dbs.default.db.user"))
val pass: String = encryptDecryptService.decrypt(config.get[String]("slick.dbs.default.db.password"))
val dbConfigOwn = dbConfigProvider.get[JdbcProfile]
val decryptedConfig = dbConfigOwn.config
.withValue("db.user", ConfigValueFactory.fromAnyRef(user))
.withValue("db.password", ConfigValueFactory.fromAnyRef(pass))
DatabaseConfig.forConfig[JdbcProfile]("", decryptedConfig)
}
}
and this:
class Reservations #Inject()(val dbConfigProvider: DatabaseConfigProvider,
config: Configuration,
customizedSlickConfig: CustomizedSlickConfig)
extends HasDatabaseConfigProviderTalachitas[JdbcProfile] {
val logger: Logger = Logger(this.getClass())
this.dbConfig = customizedSlickConfig.createDbConfigCustomized(dbConfigProvider)
needed to override completely that default slick using some https://github.com/google/tink library for encryption

AppEngine Python - how to deal with cached object values

Newbie question.
So I have this handler for a page that appends some CSS/JS files. The problem is that subsequent requests will result to values appended over and over again.
Example:
def action_index(self):
self.template = 'index.html'
extra_styles = ['/media/css/jquery.lightbox-0.5.css']
extra_scripts = ['/media/js/jquery.lightbox-0.5.min.js', '/media/js/project.js']
for style in extra_styles:
self.styles.append(style)
for script in extra_scripts:
self.scripts.append(script)
How do you usually handle this in platform like Google AppEngine since I'm coming from PHP background where objects only lives within the current request.
Thanks
As requested, here is the base class:
Base controller
from google.appengine.ext.webapp import template
import os
import config
import datetime
class BaseController(object):
request = None
response = None
action = 'index'
method = 'get'
params = []
template_values = {}
template_dir = None
template = None
default_styles = ['/media/bootstrap/css/bootstrap.min.css', '/media/css/style.css']
default_scripts = ['/media/js/jquery-1.6.4.min.js']
styles = []
scripts = []
def __init__(self, request, response, *args, **kwargs):
self.request = request
self.response = response
self.action = 'index'
if 'action' in kwargs and kwargs['action']:
self.action = kwargs['action']
self.method = 'get'
if 'method' in kwargs and kwargs['method']:
self.method = kwargs['method']
self.params = []
if 'params' in kwargs and kwargs['params']:
if isinstance(kwargs['params'], list):
self.params = kwargs['params']
# Initialize template related variables
self.template_values = {}
self.styles = list(self.default_styles)
self.scripts = list(self.default_scripts)
def pre_dispatch(self):
pass
def post_dispatch(self):
if self.template is not None and self.template_dir is not None:
# Populate current year
dt = datetime.date.today()
self.template_values['current_year'] = dt.year
# Populate styles and scripts
self.template_values['styles'] = self.styles
self.template_values['scripts'] = self.scripts
path = os.path.join(config.template_dir, self.template_dir, self.template)
self.response.out.write(template.render(path, self.template_values))
def run_action(self):
action_name = 'action_' + self.action
if hasattr(self, action_name):
action = getattr(self, action_name)
action()
else:
raise Http404Exception('Controller action not found')
def dispatch(self):
self.pre_dispatch()
self.run_action()
self.post_dispatch()
class HttpException(Exception):
"""Http Exception"""
pass
class Http404Exception(HttpException):
"""Http 404 exception"""
pass
class Http500Exception(HttpException):
"""Http 500 exception"""
pass
And the child class
import datetime
from dclab.lysender.controller import BaseController
class ProjectsController(BaseController):
template_dir = 'projects'
def action_index(self):
self.template = 'index.html'
self.styles.extend(['/media/css/jquery.lightbox-0.5.css'])
self.scripts.extend([
'/media/js/jquery.lightbox-0.5.min.js',
'/media/js/project.js'
])
My fault was that I'm assigning a list to another list via reference, not cloning the list. I'm not so aware of this behavior that's why it made me scratching my head the whole night.
You're declaring a whole lot of variables directly under the class definition. Doing this does not define instance members, as you seem to be expecting, but rather defines class variables - the equivalent of 'static' in a language like Java.
Instead of declaring things as class variables, initialize your values inside the constructor - the __init__ method of your class.
I would also strongly recommend using an existing web framework like webapp2 rather than inventing your own.

Resources