I have an API that returns a JSON object as such:
[
{
"id": 2435,
"name": "First Last",
"email": "xxx#gmail.com",
"gender": "male",
"status": "inactive"
},
...
And I'm attempting to populate the response into a struct list variable (users) using Chainlink any API.
Please find below the contract code :
pragma solidity ^0.8.7;
import '#chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '#chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
contract FetchFromArray is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
string public jsonData;
bytes32 private jobId;
uint256 private fee;
User[] public users;
struct User{
uint256 id;
uint256 name;
uint256 email;
uint256 gender;
uint256 status;
}
event GetUsersData(bytes32 indexed requestId, string jsonData);
/**
* #notice Initialize the link token and target oracle
*
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
jobId = '7d80a6386ef543a3abb52817f6707e3b';
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response
*/
function getUser() public returns (bytes32 requestId){
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
req.add('get', 'https://gorest.co.in/public/v2/users');
req.add('path', '$');
// Sends the request
return sendChainlinkRequest(req, fee);
}
/**
* Receive the response in the form of string
*/
function fulfill(bytes32 _requestId, string memory _jsonData) public recordChainlinkFulfillment(_requestId) {
emit GetUsersData(_requestId,_jsonData);
jsonData = _jsonData;
// users = TO DO popoluate users list
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
How do I add my json object to a struct?
Questions :
1 - Which jsonpath expression to use to retrieve the full json response of the api ? I tried req.add('path', '$') and req.add('path', '*'), but both didn't return the json.
2 - Once the full json is retrieved and stored in jsonData variable, how to parse it and insert the result in "User[] public users" variable ?
Related
I have two questions.
1) I am getting the following error:
TypeError: Contract "MyToken" should be marked as abstract.
--> contracts/MyToken.sol:8:1:
According to my understanding, contract should be abstract when there is a unimplemented function. Here I have the function foo. But still getting this error?
2) Also I want write a constructor which passes totalSupply_ to the contract. Is it possible to implement in the way I have done?
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
//import '#openzeppelin/contracts/token/ERC20/IERC20.sol';
import '#openzeppelin/contracts/token/ERC20/ERC20.sol';
contract MyToken is ERC20 {
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_,string memory symbol_, uint totalSupply_ ) {
_name = name_;
_symbol = symbol_;
_totalSupply = totalSupply_;
}
function foo() external returns (uint) {
uint temp;
temp = 1+1;
return temp;
}
}
You are inheriting from ERC20 but you are not calling its constructor
constructor(string memory name_,string memory symbol_,uint totalSupply_)ERC20("name","SYM") {
_name = name_;
_symbol = symbol_;
_totalSupply = totalSupply_;
}
In your case you have to call ERC20("name","SYM") because ERC20 is inheriting from an abstract Context class.
contract ERC20 is Context, IERC20, IERC20Metadata {
if you did not inherit from Context you would not have to call ERC20("name","SYM")
contract ERC20 is IERC20, IERC20Metadata {
Since you are calling ERC20("name","SYM") you are actually setting name and symbol so you dont have to set them in MyToken constructor:
uint256 private _totalSupply;
constructor(uint totalSupply_ )ERC20("name","SYM") {
_totalSupply = totalSupply_;
}
Try this:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
//import '#openzeppelin/contracts/token/ERC20/IERC20.sol';
import '#openzeppelin/contracts/token/ERC20/ERC20.sol';
contract MyToken is ERC20 {
uint256 private _totalSupply;
constructor(string memory name_,string memory symbol_, uint totalSupply_ ) ERC20(name_, symbol_) {
_totalSupply = totalSupply_;
}
function foo() external returns (uint) {
uint temp;
temp = 1+1;
return temp;
}
}
I have a URL of the following form:
GET /cat/{cat}/meows/{overTime}
I can write a controller method that looks like this in order to capture the values:
public function getMeowsOverTime(Cat cat, int overTime)
What I would like is to validate the {cat} and {overTime} values before it gets to the controller method.
I have tried creating a custom MeowsOverTimeRequest object that extends Illuminate\Http\Request according to the recommendation here: Laravel 5.7 - Override all() method in Request validation Class to validate route parameters?
So that would be:
class MeowsOverTimeRequest extends Request
{
public function authorize()
{
return true;
}
public function all($keys = null)
{
$request = parent::all($keys);
$request['cat'] = $this->route('cat');
$request['overTime'] = $this->route('overTime');
return $request;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'cat' =>
[
'required',
'integer'
],
'overTime' =>
[
'required',
'integer',
Rule::in(Cat::getMeowTimeOptions())
]
];
}
}
But $this->route('cat') and $this->route('overTime') both return null. How can I access these variables?
I would prefer not to use regular expression validation due to the overTime being limited by what is returned from Cat::getMeowTimeOptions
I want to use NextFunction(next() for accessing next middleware) in the member functions of the class for redirecting to next middleware after sending mail.
File : Errors.ts
import { NextFunction, Request, Response } from 'express';
export class SettlementErrors extends Error {
/**
* Constructs the SettlementErrors class
* #param {String} message an error message
* #param {Error} errorStack Error Stack
* #constructor
*/
private errorstack;
constructor(message:string,errorStack:Error) {
super(message);
this.errorstack = errorStack;
this.sendEmail();//Call this function without sending req,res,next as params
}
//Send Error Mail Want to access **next** Here
async sendEmail(request: Request, response: Response, next:NextFunction) : void
{
/**
Code For Sending Email
*/
}
}
How can I access next for switching to next middleware in member functions(function sendEmail) of this class ??
Thanks in advance
I have a controller method which I am using to "collect" variables to be assigned to template. I have overridden controller's render() method to merge "collected" and render parameters and assign them to template.
Example:
class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
private $jsVars = [];
protected function addJsVar($name, $value)
{
$this->jsVars[$name] = $value;
}
public function render($view, array $parameters = [], Response $response = null)
{
return parent::render($view, array_merge($parameters, ['jsVars' => $this->jsVars], $response);
}
public function indexAction()
{
// collect variables for template
$this->addJsVar('foo', 'bar');
return $this->render('#App/index.html.twig', ['foo2' => 'bar2']);
}
}
I just upgraded to Symfony 3.4 which complains that since Symfony4 I am not allowed to override render() method as it will be final.
How could I make it work seamlessly, i.e without defining a new method?
I know about Twig globals but these dont help me
I could use a service to collection variables and inject that service to Twig but that seems odd
Are there events I could listen, e.g TwigPreRender or smth?
You can render a controller from inside Twig like that:
{{ render(controller('App\\Controller\\YourController::yourAction', { 'args': 'hi' })) }}
Documentation here
Seems that there is no easy way.
Basically there are 2 options:
create your own template engine by extending current Symfony\Bundle\TwigBundle\TwigEngine
decorate current templating engine service templating.engine.mytwig
I chose the latter.
Few explanations:
I created service templating.engine.mytwig which decorates current engine templating.engine.twig. Class will get current ´TwigEngine` as input and I'll delegate most of the stuff to it
The class also needs to be twig extension by implementing \Twig_ExtensionInterface (or extending \Twig_Extension was sufficient for me). Also service needs to have tag twig.extension. Otherwise you'll end up having errors such as "Cannot find private service 'assetic' etc"
setParameter/getParameter are for collecting and returning parameters
Then I added shortcut methods to my Controller - setJsVar
Twig template requires also handling of those variables, preferably somewhere in the layout level. But that is not included here
One could you this solution to collect arbitrary template parameters, e.g if you want to assign from another method or whatever
It would be good idea to clear collected parameters after render
Was that all worth it? I dont know :) Cannot understand why Symfony team chose to make Controller::render final in the first place. But anyway here it is:
TwigEnging class:
namespace My\CommonBundle\Component\Templating\MyTwigEngine;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpFoundation\Response;
class MyTwigEngine extends \Twig_Extension implements EngineInterface
{
/**
* #var TwigEngine $twig Original Twig Engine object
*/
private $twig;
/**
* #var array $parameters Collected parameters to be passed to template
*/
private $parameters = [];
/**
* MyTwigEngine constructor.
*
* #param TwigEngine $twig
*/
public function __construct(TwigEngine $twig)
{
$this->twig = $twig;
}
/**
* "Collects" parameter to be passed to template.
*
* #param string $key
* #param mixed $value
*
* #return static
*/
public function setParameter($key, $value)
{
$this->parameters[$key] = $value;
return $this;
}
/**
* Returns "collected" parameter
*
* #param string $key
* #return mixed
*/
public function getParameter($key, $default = null)
{
$val = $this->parameters[$key] ?? $default;
return $val;
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
* #param array $parameters
*
* #return string
* #throws \Twig\Error\Error
*/
public function render($name, array $parameters = array())
{
return $this->twig->render($name, $this->getTemplateParameters($parameters));
}
/**
* #param string $view
* #param array $parameters
* #param Response|null $response
*
* #return Response
* #throws \Twig\Error\Error
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
return $this->twig->renderResponse($view, $this->getTemplateParameters($parameters), $response);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function exists($name)
{
return $this->twig->exists($name);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function supports($name)
{
return $this->twig->supports($name);
}
/**
* #param $name
* #param array $parameters
*
* #throws \Twig\Error\Error
*/
public function stream($name, array $parameters = array())
{
$this->twig->stream($name, $this->getTemplateParameters($parameters));
}
/**
* Returns template parameters, with merged jsVars, if there are any
* #param array $parameters
* #return array
*/
protected function getTemplateParameters(array $parameters = [])
{
$parameters = array_merge($this->parameters, $parameters);
return $parameters;
}
}
Decorator service (services.yml):
services:
templating.engine.mytwig:
decorates: templating.engine.twig
class: My\CommonBundle\Component\Templating\MyTwigEngine
# pass the old service as an argument
arguments: [ '#templating.engine.mytwig.inner' ]
# private, because you probably won't be needing to access "mytwig" directly
public: false
tags:
- { name: twig.extension }
Base controller alteration:
namespace My\CommonBundle\Controller;
use My\CommonBundle\Component\Templating\MyTwigEngine;
abstract class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/**
* Allows to set javascript variable from action
*
* It also allows to pass arrays and objects - these are later json encoded
*
* #param string $name Variable name
* #param mixed $value - string|int|object|array
*
* #return static
*/
protected function setJsVar($name, $value)
{
/** #var MyTwigEngine $templating */
$templating = $this->getTemplating();
if (!$templating instanceof MyTwigEngine) {
throw new \RuntimeException(sprintf(
'Method %s is implemented only by %s', __METHOD__, MyTwigEngine::class
));
}
$jsvars = $templating->getParameter('jsVars', []);
$jsvars[$name] = $value;
$templating->setParameter('jsVars', $jsvars);
return $this;
}
/**
* Returns templating service
* #return null|object|\Twig\Environment
*/
private function getTemplating()
{
if ($this->container->has('templating')) {
$templating = $this->container->get('templating');
} elseif ($this->container->has('twig')) {
$templating = $this->container->get('twig');
} else {
$templating = null;
}
return $templating;
}
}
I've written most of our project's jobs/pipelines in DSL without any previous groovy experience but now I'm stuck at more advanced problem that I can't figure out.
I'm trying to implement a method that would add 1,2,n promotions to a job.
Below you can see a fully-working method that can add one promotion, and I expected it to work in such way that I'd just call the method twice if I needed another one but then I ran into my problem - only promotion that was created the latest would be generated.
/**
* #param job DSL job object
* #param promotionName Name of the promotion
* #param nextJobs Comma seperated string of jobs to trigger when promotion is executed
* #param deployers Comma seperated string IDs that can execute promotion
* #param params Array of parameters to pass to the next job [0] = key, [1] = value.
*/
static void addPromotion(def job, String promotionName, String nextJobs, String deployers, String[][] params){
job.properties {
promotions {
promotion {
name(promotionName)
icon("star-gold")
conditions {
manual(deployers)
}
actions {
downstreamParameterized {
trigger(nextJobs) {
parameters {
for (String[] param : params){
predefinedProp(param[0]+"=",param[1])
}
}
}
}
}
}
}
}
}
The way it would work, however, if I added another 'promotion' closure like this, however, this example would generate almost identical(name and name-1) promotions:
static void addPromotion(def job, String promotionName, String nextJobs, String deployers, String[][] params){
job.properties {
promotions {
promotion {
name(promotionName)
icon("star-gold")
conditions {
manual(deployers)
}
actions {
downstreamParameterized {
trigger(nextJobs) {
parameters {
for (String[] param : params){
predefinedProp(param[0]+"=",param[1])
}
}
}
}
}
}
promotion {
name("${promotionName}-1")
icon("star-gold")
conditions {
manual(deployers)
}
actions {
downstreamParameterized {
trigger(nextJobs) {
parameters {
for (String[] param : params){
predefinedProp(param[0]+"=",param[1])
}
}
}
}
}
}
}
}
}
Is it possible to re-use closures in some way and populate the variables from a different method maybe? Or any other ideas?
This is how I solved it.
Generic promotion object part:
/**
* Adds 'promoted-builds' plugin configuration to job
**/
class Promotions {
public def job
public String promotionName
public String nextJobs
public String deployers
public String [][] params
/**
* #param job DSL job object
* #param promotionName Name of the promotion
* #param nextJobs Comma seperated string of jobs to trigger when promotion is executed
* #param deployers Comma seperated string IDs that can execute promotion
* #param params Array of parameters to pass to the next job [0] = key, [1] = value.
*/
public Promotions(Object jobName, String promotionName, String nextJobs, String deployers, String[][] params){
this.job = jobName
this.promotionName = promotionName
this.nextJobs = nextJobs
this.deployers = deployers
this.params = params
}
static void addPromotions(Promotions ... jobPromotions){
// Assuming the same job is provided as arguments
jobPromotions[0].job.properties {
promotions {
for (Promotions jobPromotion : jobPromotions){
promotion {
name(jobPromotion.promotionName)
// star-gold, star-silver
icon("star-gold")
conditions {
manual(jobPromotion.deployers)
}
actions {
downstreamParameterized {
trigger(jobPromotion.nextJobs) {
parameters {
for (String[] param : jobPromotion.params){
predefinedProp(param[0],param[1])
}
}
}
}
}
}
}
}
}
}
}
And then I prepare my params and pass them to the promotion constructor, and in the end I call addPromotions() and pass all my constructed objects to it:
def nextJobs = "${Configuration.repoName}-${branchName}-deploy-to-perf"
def deployers = "developer"
def params = [["VERSION", "\${VERSION}"],
["SOURCE_GIT_COMMIT", "\${SOURCE_GIT_COMMIT}"]] as String[][]
def promo1 = new Promotions(job, "Promote to PERF", nextJobs, deployers, params)
def nextJobs2 = "../master/${Configuration.repoName}-${branchName}-to-prod-dtr"
def deployers2 = "admin"
def params2 = [["VERSION", "\${VERSION}"],
["SOURCE_GIT_COMMIT", "\${SOURCE_GIT_COMMIT}"]] as String[][]
def promo2 = new Promotions(job, "Promote to PROD", nextJobs2, deployers2, params2)
Promotions.addPromotions(promo1, promo2)