I have the following code which creates a yaml file from dictionary:
import yaml
from collections import OrderedDict
import json
from pprint import pprint
import random
import string
data = {
"test_name" : "Create_user test",
"stages":{
"name" : "check user sucessfully added",
"request": {
"url":"self.url",
"json":{
"username":"self.username",
"role":"self.role",
"userProfile":"self.userProfile"
},
"method":"self.method",
"headers":{"content_type":"application/json"}
},
"response" : {"status_code":200}
}
}
print(data)
def setup_yaml():
""" https://stackoverflow.com/a/8661021 """
represent_dict_order = lambda self, data: self.represent_mapping('tag:yaml.org,2002:map', data.items())
yaml.add_representer(OrderedDict, represent_dict_order)
setup_yaml()
with open('abc_try.tavern.yml', 'w') as outfile:
yaml.dump(OrderedDict(data), outfile, default_flow_style=False)
And I get the 'abc_try.tavern.yml' file as:
test_name: Create_user test
stages:
name: check user sucessfully added
request:
headers:
content_type: application/json
json:
role: self.role
userProfile: self.userProfile
username: self.username
method: self.method
url: self.url
response:
status_code: 200
but the I want the following file to be generated:
test_name: Create_user test
stages:
- name: check user sucessfully added
request:
headers:
content_type: application/json
json:
role: self.role
userProfile: self.userProfile
username: self.username
method: self.method
url: self.url
response:
status_code: 200
How to add the '-' in third line before 'name'.(Kindly mind the spacing and formatting of '-', i.e. just below 's' of 'stages'.
The '-' indicates a list element. So you have to put the inner dict in a list:
data = {
"test_name" : "Create_user test",
"stages": [
{
"name" : "check user sucessfully added",
# ...
}
]
}
Related
I'm using BasePermission decorator as specified in documentation.
#strawberry.type
class Query:
#strawberry.field(permission_classes=[IsAuthenticated])
def user(self) -> User:
# get by token OFC
return User(user_id=1, email="vladimir#cw.tech", first_name = "Vladimir", last_name = "Kirilov")
In my impementation I use VerifyToken class as described in FastAPI auth0 documentation.
class IsAuthenticated(BasePermission):
message = "User is not authenticated"
def has_permission(self, source: Any, info: Info, **kwargs) -> bool:
print(source)
print(info)
token: str = Depends(token_auth_scheme)
print(token)
result = VerifyToken(token.credentials).verify()
if result.get("status"):
print(result)
return False
return True
So I'm trying to get and verify the BEARER from the request, but I'm not able to extract it to process it further and get the error, please advise.
{
"data": null,
"errors": [
{
"message": "'Depends' object has no attribute 'credentials'",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"user"
]
}
]
}
Figured it out, not the cleanest way, but here it is
class IsAuthenticated(BasePermission):
message = "User is not authenticated"
async def has_permission(self, source: Any, info: Info, **kwargs) -> bool:
request: Union[Request, WebSocket] = info.context["request"]
print(request.headers)
if "Authorization" in request.headers:
print(request.headers['Authorization'])
result = VerifyToken( request.headers['Authorization'][7:] ).verify()
if result.get("status") == "error":
print(result.get("msg"))
return False
if result.get("sub"):
print(result)
return True
return False
I have figured out that i can convert yaml to json pretty easily. Which will allow me to have the payload of to be created and sent from the yaml file and updated there as well.
What am i trying to accomplish?
Ideally i have multiple objects within a yaml file which i need to iterate over. Each time i iterate over them they need to be sent as a payload for an api request. I would like to convert the yaml to json and provide that as the payload vs doing the for loop and setting all the values manually.
sample.yaml
Component: 'users'
user:
username: 'rhendricks'
email: 'rich#piedpiper.com'
receiveNotifications: False
firstName: 'Richard'
lastName: 'Hendricks'
password: 'Pass321!'
roles:
- id: 105
- id: 106
user:
username: 'dinesh'
email: 'dinesh#piedpiper.com'
receiveNotifications: False
firstName: 'Dinesh'
lastName: 'Chugtai'
password: 'Pass321!'
roles:
- id: 105
Above is the sample yaml file i am working with.
what the api payload is expecting:
spayload = json.dumps({
'user':{
'username': username,
'email': email,
'firstName': firstName,
'lastName': lastName,
'password': password,
'recieveNotifications': receiveNotifications,
'roles': [
{'id': 106},
{'id': 105}
]
}
})
My current method for setting up the payload is something like the following - this is an example of another payload. If i can't figure out another way i will result to the following.
for k, v in result['groups'].items():
name = result['groups'][k]['name']
description = result['groups'][k]['description']
location = result['groups'][k]['location']
code = result['groups'][k]['code']
payload = json.dumps({
"group":{
"name": name,
"description": description,
"location": location,
"code": code
}
})
Here is my current testing:
import json
import re
import urllib
import urllib3
import yaml
import requests
import glob
from . import morph_log
def createUser(yaml_file):
logger = morph_log.get_logger('createUser')
files = glob.glob(yaml_file)
for file in files:
yaml_file = file
logger.info('Current file: '+yaml_file)
with open(yaml_file) as f:
try:
result=yaml.safe_load(f)
except yaml.YAMLError as exc:
logger.error(exc)
logger.error('Was unable to load the file')
os_list={}
with open(yaml_file) as infile:
os_list = yaml.load(infile, Loader=yaml.FullLoader)
print(json.dumps(os_list))
yaml_file = open(yaml_file, 'r')
result = yaml_file.readlines()
del result[0]
print(result)
# for key, value in yaml.load(result, Loader=yaml.FullLoader).items():
# print('key', key)
for value in result:
print('Entry', value)
#print('entry:', value['user'])
#print(entry['username'])
#print('Sub', entry['users'])
for key, value in result['user'].items():
# username = value['username']
print('Key', key)
print('User', value )
I am also trying to drop the component line as i start to process as the api doesn't understand that construct. I use that component element as a way to process the file correctly.
The example yaml file as 2 payloads that i need to be able to send.
Here is my log of what i have so far.
2022-02-04 21:51:43,585:25:users:createUser:INFO:Current file: contentpacks/piedPiper/users.yaml
{"Component": "users", "user": {"username": "dinesh", "email": "dinesh#piedpiper.com", "receiveNotifications": false, "firstName": "Dinesh", "lastName": "Chugtai", "password": "Pass321!", "roles": [{"id": 105}]}}
['user:\n', " username: 'rhendricks'\n", " email: 'rich#piedpiper.com'\n", ' receiveNotifications: False\n', " firstName: 'Richard'\n", " lastName: 'Hendricks'\n", " password: 'Pass321!'\n", ' roles: \n', ' - id: 105\n', ' - id: 106 # needs to be some sort of list. \n', 'user:\n', " username: 'dinesh'\n", " email: 'dinesh#piedpiper.com'\n", ' receiveNotifications: False\n', " firstName: 'Dinesh'\n", " lastName: 'Chugtai'\n", " password: 'Pass321!'\n", ' roles:\n', ' - id: 105']
Entry user:
Entry username: 'rhendricks'
Entry email: 'rich#piedpiper.com'
Entry receiveNotifications: False
Entry firstName: 'Richard'
Entry lastName: 'Hendricks'
Entry password: 'Pass321!'
Entry roles:
Entry - id: 105
Entry - id: 106 # needs to be some sort of list.
Entry user:
Entry username: 'dinesh'
Entry email: 'dinesh#piedpiper.com'
Entry receiveNotifications: False
Entry firstName: 'Dinesh'
Entry lastName: 'Chugtai'
Entry password: 'Pass321!'
Entry roles:
Entry - id: 105
Traceback (most recent call last):
File "app.py", line 21, in <module>
cpack_utility.contentPack_file_processor(contentPackSelection)
File "/home/ubuntu/implementation/cpack_utility/contentPack_processing.py", line 149, in contentPack_file_processor
contentPack_implementation(contentPackSelection)
File "/home/ubuntu/implementation/cpack_utility/contentPack_processing.py", line 62, in contentPack_implementation
users.createUser(yaml_file)
File "/home/ubuntu/implementation/cpack_utility/users.py", line 50, in createUser
for key, value in result['user'].items():
TypeError: list indices must be integers or slices, not str
Since i will expect the payload to be a certain way i will no doubt have to add this to my yaml validation scripts. That parts pretty easy.
any thoughts would be greatly appreciated.
In YAML, it is an error to have multiple identical keys in a mapping. Your root mapping has user two times. This is not allowed.
It is a bit hard to figure out what you want to do, but I believe you're searching for multi-document YAML files:
Component: 'users'
---
user:
username: 'rhendricks'
email: 'rich#piedpiper.com'
receiveNotifications: False
firstName: 'Richard'
lastName: 'Hendricks'
password: 'Pass321!'
roles:
- id: 105
- id: 106
---
user:
username: 'dinesh'
email: 'dinesh#piedpiper.com'
receiveNotifications: False
firstName: 'Dinesh'
lastName: 'Chugtai'
password: 'Pass321!'
roles:
- id: 105
--- is a directives end marker which in this case effectively separates multiple YAML documents. Each document contains a root mapping with the sole key user.
This minimal script:
import yaml, json
import sys
with open("sample.yaml") as file:
content = iter(yaml.safe_load_all(file))
next(content) # skip first document
for doc in content:
json.dump(doc, sys.stdout, indent=2)
print("\n---")
Will then produce:
{
"user": {
"username": "rhendricks",
"email": "rich#piedpiper.com",
"receiveNotifications": false,
"firstName": "Richard",
"lastName": "Hendricks",
"password": "Pass321!",
"roles": [
{
"id": 105
},
{
"id": 106
}
]
}
}
---
{
"user": {
"username": "dinesh",
"email": "dinesh#piedpiper.com",
"receiveNotifications": false,
"firstName": "Dinesh",
"lastName": "Chugtai",
"password": "Pass321!",
"roles": [
{
"id": 105
}
]
}
}
---
File dir
fastapi_jwt
.env
main.py
app
api.py
model.py
auth
auth_bearer.py
auth_handler.py
fastapi_jwt/.env
secret=please_please_update_me_please
algorithm=HS256
fastapi_jwt/main.py
import uvicorn
if __name__ == "__main__":
uvicorn.run("app.api:app", host="127.0.0.1", port=8000, reload=True)
fastapi_jwt/app/api.py
from fastapi import FastAPI, Body, Depends
from starlette.responses import HTMLResponse
from app.model import PostSchema, UserSchema, UserLoginSchema
from app.auth.auth_bearer import JWTBearer
from app.auth.auth_handler import signJWT
app = FastAPI()
#app.get("/", tags=["root"])
async def read_root():
return {"message": "Welcome to your blog!."}
from app.model import PostSchema
posts = [
{
"id": 1,
"title": "Pancake",
"content": "Lorem Ipsum ..."
}
]
users = []
#app.get("/posts", tags=["posts"])
async def get_posts() -> dict:
return { "data": posts }
#app.get("/posts/{id}", tags=["posts"])
async def get_single_post(id: int) -> dict:
if id > len(posts):
return {
"error": "No such post with the supplied ID."
}
for post in posts:
if post["id"] == id:
return {
"data": post
}
#app.get("/post", dependencies=[Depends(JWTBearer())], tags=["posts"])
#app.post("/post", dependencies=[Depends(JWTBearer())], tags=["posts"])
async def add_post(post: PostSchema) -> dict:
post.id = len(posts) + 1
posts.append(post.dict())
return {"result":"Post added successfully"}
#app.post("/user/signup", tags=["user"])
async def create_user(user: UserSchema = Body(...)):
users.append(user) # replace with db call, making sure to hash the password first
return signJWT(user.email)
def check_user(data: UserLoginSchema):
for user in users:
if user.email == data.email and user.password == data.password:
return True
return False
#app.post("/user/login", tags=["user"])
async def user_login(user: UserLoginSchema = Body(...)):
if check_user(user):
return signJWT(user.email)
return {
"error": "Wrong login details!"
}
fastapi_jwt/app/model.py
from pydantic import BaseModel, Field, EmailStr
class PostSchema(BaseModel):
id: int = Field(default=None)
title: str = Field(...)
content: str = Field(...)
class Config:
schema_extra = {
"example": {
"title": "Securing FastAPI applications with JWT.",
"content": "In this tutorial, you'll learn how to secure your application by enabling authentication using JWT. We'll be using PyJWT to sign, encode and decode JWT tokens...."
}
}
class UserSchema(BaseModel):
fullname: str = Field(...)
email: EmailStr = Field(...)
password: str = Field(...)
class Config:
schema_extra = {
"example": {
"fullname": "Abdulazeez Abdulazeez Adeshina",
"email": "abdulazeez#x.com",
"password": "weakpassword"
}
}
class UserLoginSchema(BaseModel):
email: EmailStr = Field(...)
password: str = Field(...)
class Config:
schema_extra = {
"example": {
"email": "abdulazeez#x.com",
"password": "weakpassword"
}
}
fastapi_jwt/app/auth/auth_bearer.py
from fastapi import Request, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from .auth_handler import decodeJWT
class JWTBearer(HTTPBearer):
def __init__(self, auto_error: bool = True):
super(JWTBearer, self).__init__(auto_error=auto_error)
async def __call__(self, request: Request):
credentials: HTTPAuthorizationCredentials = await super(JWTBearer, self).__call__(request)
if credentials:
if not credentials.scheme == "Bearer":
raise HTTPException(status_code=403, detail="Invalid authentication scheme.")
if not self.verify_jwt(credentials.credentials):
raise HTTPException(status_code=403, detail="Invalid token or expired token.")
return credentials.credentials
else:
raise HTTPException(status_code=403, detail="Invalid authorization code.")
def verify_jwt(self, jwtoken: str) -> bool:
isTokenValid: bool = False
try:
payload = decodeJWT(jwtoken)
except:
payload = None
if payload:
isTokenValid = True
return isTokenValid
fastapi_jwt/app/auth/auth_handler.py
import time
from typing import Dict
import jwt
from decouple import config
JWT_SECRET = config("secret")
JWT_ALGORITHM = config("algorithm")
def token_response(token: str):
return {
"access_token": token
}
def signJWT(user_id: str) -> Dict[str, str]:
payload = {
"user_id": user_id,
"expires": time.time() + 600
}
token = jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
return token_response(token)
def decodeJWT(token: str) -> dict:
try:
decoded_token = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return decoded_token if decoded_token["expires"] >= time.time() else None
except:
return {}
I run 'main.py', the url
" http://127.0.0.1:8000/post " shows the output "{"detail":"Not authenticated"}".
So go to the url " http://127.0.0.1:8000/docs " for FastAPI -Swagger UI interface like
this.
Then I signup and login to get the jwt token and use that token to authenticate, then go to url
" /post " in FastAPI -Swagger UI and execute to get the output {"result": "Post added successfully"}.
But in " http://127.0.0.1:8000/post " got an error and show the same ouput "{"detail":"Not authenticated"}"
I want to create a generic endpoint definition in Fast API Python that reads URL path parameter and then calls a specific method to do a derealisation.
But I always get
422 Unprocessable Entity
So I expect that it works like so:
/answer/aaa -> handle_generic_answer -> read_item_aaa, type body to ModelAAA
/answer/bbb -> handle_generic_answer -> read_item_bbb, type body to ModelBBB
etc.
Here's the generic endpoint code:
#app.post("/answer/{type}")
def handle_generic_answer(type: str, item):
# I also tried
# def handle_generic_answer(type: str, item: Any):
# or
# def handle_generic_answer(type: str, item: Optional):
switcher = {
'aaaa': read_item_aaa,
'bbb': read_item_bbb,
'nothing': unrecognised_answer
}
func = switcher.get(type, unrecognised_answer)
print('answer >> ' + type)
func(item)
then I have separate methods called based on a type value:
def read_item_aaa(item: ModelAAA):
update_aaa(item)
return {"type": "aaa", "result": "success"}
def read_item_bbb(item: ModelBBB):
update_bbb(item)
return {"type": "bbb", "result": "success"}
and a default -
def unrecognised_answer(type):
print("unrecognised_answer")
raise HTTPException(status_code=400, detail="answer type not found")
return {}
models are defined like this:
from pydantic import BaseModel, Field
class ModelAAA(BaseModel):
field1: str
field2: list = []
But whether I call
http://localhost:8000/answer/aaa
or http://localhost:8000/answer/some-other-url
I always get 422:
{
"detail": [
{
"loc": [
"query",
"item"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
You forgot to annotate body parameter item.
Without this item is treated as query str parameter. For example:
#app.post("/answer/{type}")
def handle_generic_answer(type: str, item: Union[ModelAAA, ModelBBB]):
Geckoboard offers this documentation to connect to their datasets API, which you can see the implementation below.
var API_KEY = 'API_KEY';
var gb = require('geckoboard')(
API_KEY
);
gb.datasets.findOrCreate(
{
id: 'sales.by_day',
fields: {
quantity: {
type: 'number',
name: 'Number of sales'
},
gross: {
type: 'money',
name: 'Gross value of sales',
currency_code: "USD"
},
date: {
type: 'date',
name: 'Date'
}
}
},
function (err, dataset) {
if (err) {
console.error(err);
return;
}
dataset.put(
[
{ date: '2016-01-01', quantity: 819, gross: 2457000 },
{ date: '2016-01-02', quantity: 409, gross: 1227000 },
{ date: '2016-01-03', quantity: 164, gross: 492000 }
],
function (err) {
if (err) {
console.error(err);
return;
}
console.log('Dataset created and data added');
}
);
}
);
I'd like to see if there is a way to post this additional data via python (not using node.js). Would something like this be possible or must I use mode?
[
{ date: '2017-01-01', quantity: 1213, gross: 23423 },
{ date: '2017-01-02', quantity: 111, gross: 1313123 },
{ date: '2017-01-03', quantity: 333, gross: 21314 }
]
Update: Geckoboard now has their own official Python client library for their Datasets API https://github.com/geckoboard/geckoboard-python
It's definitely possible to use Python with Geckoboard's Datasets API. You can use any language or platform that can perform HTTPS requests with JSON - though Geckoboard has only released official library's for Ruby and Node so far.
Edit: I made a quick example below, but later found this: https://github.com/helium/geckoboard-python/
In short you just need to:
PUT with the a schema object to https://api.geckoboard.com/datasets to create a dataset
PUT with the a data array to https://api.geckoboard.com/datasets/:id to replace all data in a dataset
POST with the a data array to https://api.geckoboard.com/datasets/:id to append to the data in a dataset
DELETE to https://api.geckoboard.com/datasets/:id to delete a dataset
These requests are outlined at -- https://developer.geckoboard.com/api-reference/curl/
I haven't written much Python, so this may not be very Pythonic, but here's a way you could create a Geckoboard library in Python to do this for you, using the requests library underneath:
import requests
class Geckoboard(object):
def __init__(self, api_key):
self.api_key = api_key
def create(self, name, schema):
url = 'https://api.geckoboard.com/datasets/%s' % name
return requests.put(url, json=schema, auth=(self.api_key, ''))
def delete(self, name):
url = 'https://api.geckoboard.com/datasets/%s' % name
return request.delete(url, auth=(self.api_key, ''))
def replace(self, name, data):
url = 'https://api.geckoboard.com/datasets/%s/data' % name
return requests.put(url, json=data, auth=(self.api_key, ''))
def append(self, name, data):
url = 'https://api.geckoboard.com/datasets/%s/data' % name
return requests.post(url, json=data, auth=(self.api_key, ''))