How to create a graphql schema that can be searched on different fields? - scala

I'm testing out sangria to build a graphql/relay server. I have a very simple User class:
case class User(
id: Int,
username: String,
gender: Gender.Value)
I want to allow queries by either ID or username. I have created a schema that allows this, but the fields have different names:
val Query = ObjectType(
"Query", fields[UserRepo, Unit](
Field("user", OptionType(User),
arguments = ID :: Nil,
resolve = ctx => ctx.ctx.getUser(ctx arg ID)),
Field("userByUsername", OptionType(User),
arguments = Username :: Nil,
resolve = ctx => ctx.ctx.getUserByUsername(ctx arg Username))
))
Unfortunately, I need to query these with different fields names, user and userByUsername, e.g.:
curl -G localhost:8080/graphql
--data-urlencode 'query={userByUsername(username: "Leia Skywalker") {id, username, gender}}'
or
curl -G localhost:8080/graph
--data-urlencode "query={user(id: 1025) {id, username, gender}}"
How can I create a schema that allows a single field called user to be queried on either ID or username? E.g. both of the following should return the same user object:
curl -G localhost:8080/graphql
--data-urlencode 'query={user(username: "Leia Skywalker") {id, username, gender}}'
or
curl -G localhost:8080/graph
--data-urlencode "query={user(id: 1025) {id, username, gender}}"

I finally worked it out:
val ID = Argument("id", OptionInputType(IntType), description = "id of the user")
val Username = Argument("username", OptionInputType(StringType), description = "username of the user")
val Query = ObjectType(
"Query", fields[UserRepo, Unit](
Field("user", OptionType(User),
arguments = List(ID, Username),
resolve = ctx => ctx.ctx.getUser(ctx.argOpt(ID), ctx.argOpt(Username)))
))
And getUser looks like:
def getUser(id: Option[Int], username: Option[String]): Option[User] = {
if (id.isEmpty && username.isEmpty) {
None
}
else {
id.flatMap(i => users.find(_.id == i))
.orElse(username.flatMap(u => users.find(_.username == u)))
}
}

Related

Make JSON Parsing & Error Handling More Functional in Scala

I have the following piece of code that I use to read the incoming JSON which looks like this:
{
"messageTypeId": 2,
"messageId": "19223201",
"BootNotification" :
{
"reason": "PowerUp",
"chargingStation": {
"serialNumber" : "12345",
"model" : "",
"vendorName" : "",
"firmwareVersion" : "",
"modem": {
"iccid": "",
"imsi": ""
}
}
}
}
I have the following reads using play-json that would process this JSON:
implicit val ocppCallRequestReads: Reads[OCPPCallRequest] = Reads { jsValue =>
val messageTypeId = (jsValue \ 0).toOption
val messageId = (jsValue \ 1).toOption
val actionName = (jsValue \ 2).toOption
val payload = (jsValue \ 3).toOption
(messageTypeId.zip(messageId.zip(actionName.zip(payload)))) match {
case Some(_) => JsSuccess(
OCPPCallRequest( // Here I know all 4 exists, so safe to call head
messageTypeId.head.as[Int],
messageId.head.as[String],
actionName.head.as[String],
payload.head
)
)
case None => JsError( // Here, I know that I have to send a CallError back!
"ERROR OCCURRED" // TODO: Work on this!
)
}
}
It is not playing nicely when it comes to delivering the exact error message for the case None. It is all in or nothing, but what I want to avoid is that in my case None block, I would like to avoid looking into each of the Option and populate the corresponding error message. Any ideas on how I could make it more functional?

How can I parameterise information in Gatling scenarios

I need to send specific parameters to a scenario that is being reused multiple times with different payloads depending on the workflows. The following is the code that is to be reused:
var reqName = ""
var payloadName = ""
lazy val sendInfo: ScenarioBuilder = scenario("Send info")
.exec(session => {
reqName = session("localReqName").as[String]
payloadName = session("localPayloadName").as[String]
session}
)
.exec(jms(s"$reqName")
.send
.queue(simQueue)
.textMessage(ElFileBody(s"$payloadName.json"))
)
.exec(session => {
val filePath = s"$payloadName"
val body = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource(filePath).toURI)))
logger.info("timestamp value: " + session("timestamp").as[String])
logger.debug("Template body:\n " + body)
session
})
I know that you can chain scenarios in Scala/Gatling but how can I pass in information like reqName and payloadName down the chain, where reqName is a parameter to indicate the name of the request where the info is being sent and payloadName is the name of the actual JSON payload for the related request:
lazy val randomInfoSend: ScenarioBuilder = scenario("Send random info payloads")
.feed(csv("IDfile.csv").circular)
.randomSwitch(
(randomInfoType1prob) -> exec(
feed(timeFeeder)
.exec(session => {
payloadName = "Info1payload.json"
reqName ="Info1Send"
session.set("localReqName", "Info1Send")
session.set("localPayloadName", "Info1payload.json")
session
})
.exec(sendInfo)
),
(100.0 - randomInfoType1prob) -> exec(
feed(timeFeeder)
.exec(session => {
payloadName = "Info2Payload.json"
reqName ="Info2Send"
session.set("localReqName", "Info2Send")
session.set("localPayloadName", "Info2Payload.json")
session
})
.exec(sendInfo)
)
I attempted the above but the values of that 2 specific parameters were not passed through correctly. (The IDs and timestamps were fed through correctly though) Any suggestions?
Please properly read the Session API documentation. Session is immutable so Session#set returns a new instance.

Flask PyMongo find_one() return None even when data exists

I just started using MongoDb and I am stuck when using find_one(). The find() function works perfectly but None type is returned when using find_one(). I am stuck at this point. Below is the code that I am using.
#mongo.py
#app.route('/update_user/<emp_id>', methods=['GET', 'POST'])
def update_user(emp_id):
print("Employee ID:", emp_id)
output = list(emp.find_one({'emp_id': emp_id}))
print("Output:", output)
if request.method == 'POST':
if not request.form.get['emp_id'] or not request.form.get['first_name'] or not request.form.get['last_name'] \
or not request.form.get['dept_name'] or not request.form.get['gender'] or not request.form.get['dob'] \
or not request.form.get['salary'] or not request.form['country_code'] \
or not request.form.get['mobile_no']:
flash('Please enter all the fields', 'error')
else:
emp_id = int(request.form.get('emp_id'))
first_name = request.form.get('first_name')
last_name = request.form.get('last_name')
dept_name = request.form.get('dept_name')
gender = request.form.get('gender')
dob = request.form.get('dob')
salary = float(request.form.get('salary'))
country_code = int(request.form['country_code'])
mobile_no = int(request.form.get('mobile_no'))
emp.update_one({"emp_id": emp_id, "first_name": first_name, "last_name": last_name,
"dept_name": dept_name, "gender": gender, "dob": dob, "salary": salary,
"country_code": country_code, "mobile_no": mobile_no})
flash('Employee Updated')
return redirect('user.html')
return render_template('update_user.html', emp_id=emp_id, output=output)
It throws the below error:
File "D:\projects\EmpMgmtFlask\mongo.py", line 37, in update_user
output = list(emp.find_one({'emp_id': emp_id}))
TypeError: 'NoneType' object is not iterable
find_one() doesn't return a cursor; it returns a single dictionary; so you don't need to wrap it in a list function.
output = emp.find_one({'emp_id': emp_id})

Momoko, Jinja2 and Tornado

there is something fundamentally wrong with my code. These are my
tornado handlers with basic authentication and jinja2 as template
engine. The following works without the momoko db parts.
class BaseHandler(tornado.web.RequestHandler):
#property
def db(self):
return self.application.db
def get_current_user(self):
return self.get_secure_cookie("user")
class TemplateHandler(BaseHandler):
"""Request handler for writing HTML templates."""
def render(self, template_name, **kwargs):
"""Renders a Jinja2 template."""
kwargs['options'] = options.as_dict()
template = templates.environment.get_template(template_name)
html = template.render(kwargs)
self.write(html)
class AuthLoginHandler(TemplateHandler):
def get(self):
try:
errormessage = self.get_argument("error")
except:
errormessage = ""
self.render("login.html", errormessage = errormessage)
def check_permission(self, password, username):
if username == "admin" and password == "admin":
return True
return False
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
auth = self.check_permission(password, username)
if auth:
self.set_current_user(username)
self.redirect(self.get_argument("next", u"/"))
else:
error_msg = u"?error=" + tornado.escape.url_escape("Login incorrect")
self.redirect(u"/auth/login/" + error_msg)
def set_current_user(self, user):
if user:
self.set_secure_cookie("user", tornado.escape.json_encode(user))
else:
self.clear_cookie("user")
class AuthLogoutHandler(TemplateHandler):
def get(self):
self.clear_cookie("user")
self.redirect(self.get_argument("next", "/"))
class MainHandler(TemplateHandler):
#gen.engine
def get(self):
username = tornado.escape.xhtml_escape(self.current_user)
try:
cursor = yield momoko.Op(self.db.execute, 'SELECT * FROM products;')
except Exception as error:
self.write(str(error))
res = 'Query results: '+''.join(str(cursor.fetchall()))
self.render("index.html", username = username, cip = self.request.remote_ip, res = res)
For the logged in client, this code should execute a basic query and then print the result to the defined location ( {{ res }} ) within the jinja template. When I try to start the server, I get this:
line 22, in render
kwargs['options'] = options.as_dict()
AttributeError: 'module' object has no attribute 'as_dict'
We'll need to see your "import" statements to know what's wrong for certain. I suspect you have:
from tornado import options
But you need:
from tornado.options import options

About interacting and testing with django tastypie

I read many tutorial about django testing but I don't know how can I test for function insde resource , for example this User resource with signin function and obj_create. I really appreciate any helps because I can not figure out how to test them. k thanks.
class UserResource(ModelResource):
school = fields.ToOneField('frittie.app.api.api.LocationResource', 'user')
class Meta:
queryset = UserProfile.objects.all()
resource_name = 'User'
allowed_methods = ['get','post']
serializer = Serializer(formats=['json', 'plist'])
authorization= Authorization()
#models.signals.post_save.connect(create_api_key, sender=User)
#fields = ['username', 'email']
def obj_create(self, bundle, request=None, **kwargs):
if not request.method == "POST":
raise BadRequest('Object not found or not allowed to create a new one.')
username, email, password = bundle.data['username'], bundle.data['password'], bundle.data['password'],
try:
bundle.obj = User.objects.create_user(username, email, password)
except IntegrityError:
raise BadRequest('That username already exists')
return bundle
def signin(self, request, **kwargs):
self.method_check(request, allowed=['post'])
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return self.create_response(request, {'success': True})
else:
# Return a 'disabled account' error message
return self.create_response(request, {'success': False})
else:
# Return an 'invalid login' error message.
return self.create_response(request, {'success': False})
Tastypie has a descent testing documentation - ResourceTestCase API Reference