I am trying to build a simple restapi for my koa server.
here is my code
const Koa = require('koa');
const app = new Koa();
const json = require('koa-json')
let Router = require('koa-router');
let router = new Router();
let users = [
{ name: 'Kevin', email: 'k#gmail.com' },
{ name: 'Star', email: 'sss#gmail.com' },
{ name: 'Vick', email: 'V#gmail.com' },
]
router
.get('/user', list)
.get('/user/:id', detail)
.post('/user/:id', update)
.post('/user/create', create)
function list(ctx) {
ctx.body = users
}
function detail(ctx) {
ctx.body = users[ctx.params.id]
}
function create(ctx) {
new_user = ctx.request.body
users.push(new_user)
ctx.body = users
}
function update(ctx) {
ctx.body = Object.assign(users[ctx.params.id], ctx.request.body)
}
When I try to run the post create method, I got this error.
TypeError: Cannot convert undefined or null to object
at Function.assign (<anonymous>)
at update (E:\project\javascript\nodejs\KoaAPI\person\server.js:38:20)
I did use the post method and the url entered was user/create, I didn't even run the post update method.
However, when I switch the code order, put the post create before update like this:
router
.get('/user', list)
.get('/user/:id', detail)
.post('/user/create', create)
.post('/user/:id', update)
It works all fine. Can someone explain me why? Thank you.
The reason is that the router invokes the first matching URL (with the same HTTP method) according to the routes declaration. In your case the HTTP handler for .post('/user/:id') will be invoked when the URL is /user/create because the router tries to match the URL in the order of the routes declaration. The first regular expression to be checked is /user/.* ('/user/:id' will be roughly translated to that) and it matches the string /user/create so it will skip checking the rest of the route declarations (it won't check specifically for .post('/user/create') no matter it's a better match logically) and will immediately invoke the first found handler.
The fix as you correctly observed is to declare the more specific routes first - if you declare /user/create before /user/:id, the request to /user/create will invoke the handler you initially expected.
Related
I'm trying to use Environment Variables via plugin method (https://docs.cypress.io/api/plugins/configuration-api#Switch-between-multiple-configuration-files), this has worked previously but recently has stopped working for me, any pointers would be great.
plugin/index.js
const fs = require('fs-extra')
const path = require('path')
function getConfigurationByFile(file) {
const pathToConfigFile = path.resolve('cypress', 'config', `${file}.json`)
return fs.readJson(pathToConfigFile)
}
module.exports = (on, config) => {
const file = config.env.configFile || 'build'
return getConfigurationByFile(file)
}
ConfigFile > build.json
{
"env": {
"StudentPortal": "https://www.google.co.uk"
}
}
Usage
cy.visit(Cypress.env('StudentPortal'));
As I said, this used to work and would visit the URL within the configFile, now I just get the following error:
CypressError
cy.visit() must be called with a url or an options object containing a url as its 1st argumentLearn more
cypress/support/commands.js:17:8
15 | Cypress.Commands.add('StudentPortalLogin', (email, password) => {
16 |
17 | cy.visit(Cypress.env('StudentPortal'));
It appears the baseURL may be missing. cy.visit() is looking for something in reference to the baseURL. I don't see one defined in the build.json file, that might fix the error to add a baseURL, "https://www.google.co.uk", then put authentication parameters inside the env{} part, per the example in your link.
I am trying to create some kind of generic http service in angular + ts, where I provide model type and it should be able connect to corresponding endpoint.
I have following working solution:
type Type<T> = new (...args: any[]) => T;
public getList<T>(c: Type<T>): Observable<T[]> {
const service = this.getService<T>(c);
return service.getList();
}
private getService<T>(c: Type<T>) {
const match = this.serviceMapping.find(i => c === i.type);
if (!match) {
throw new Error('No service registered for type ' + c.name);
}
return match.service;
}
I have created mapping object to map provided objects to services
this.serviceMapping = [
{
type: Customer, // object class
service: customerService, // injected singleton instance of http service
},
]
and getList is called from component e.g.:
genericService.getList<Customer>(Customer).subscribe(...)
This is working fine and service returns results, but I do not like that I have to provide Customer type as method parameter. I would like to remove it from method parameters and leave parameters only for request related data (query parameters, body, ...).
Is it somehow possible?
I would like to use it like this:
genericService.getList<Customer>().subscribe(res => this.customers = res);
genericService.getById<Customer>(customerId).subscribe(...)
genericService.create<Customer>(customerData).subscribe(...)
genericService.getList<Foo>(customerData).subscribe(res => this.foos = res;)
genericService.getList<Bar>(customerData).subscribe(res => this.bars = res)
(Of course that I will make sure that every service will provide those methods. This should handle default CRUD operations).
Thank you
As of typescript 2.8, this is possible. You can get the return type of the function.
Refer to this merged PR - https://github.com/Microsoft/TypeScript/pull/21847
you can create a common method which passes the function as a parameter and check for the type.
I have a file I wish to test, with an http-client that should be mocked:
schema.js:
const { http } = require('#foo/http-client');
.......
const result = await http.foo('another-service').get('graphql', {
query: `
{
SomeResolver(clientId: "${args.clientId}") {
id
start
end
}
}
`,
});
I need to mock the result.
schema.test.js:
const sinon = require('sinon');
const mockHttp = sinon.mock(require('#foo/http-client'));
.........
mockHttp.expects('foo').returns({
get: (x,y) => {
// mock the response here
},
});
TypeError: Attempted to wrap undefined property foo as function is the error with the above which makes sense because http is destructured.
However, if I change the expects line to:
mockHttp.expects('http').returns
I get the error TypeError: Attempted to wrap object property http as function which also makes sense because http is a property.
As you can see I'm quite new to Sinon but my question is how can I mocked http.foo.get when http is a property?
To my understanding, mocks are for objects, and expectations are for functions - one per each.
In your context, it seems you could do:
const { http } = require('#foo/http-client');
const mockHttp = sinon.mock(require('#foo/http-client').http); // mind .http
mockHttp.expects('foo').returns({
get: (x,y) => {
// mock the response here
},
});
http.foo().get();
mockHttp.verify();
If you need to set expectations on other http functions, you can reuse mockHttp. If you need expectations on functions somewhere else in the module, you'll need to create another mock for the object they're declared in, and verify() that other mock as well. (That's how I understand it anyway, someone please correct if you know better!)
In any case, using destructuring assignments won't change the fact that your http variable references the module's http property. In other words:
const { http } = require('#foo/http-client');
console.log( http === require('#foo/http-client').http ); // true
From the Firebase note:
Given a single key path like alanisawesome, updateChildren() only updates data at the first child level, and any data passed in beyond the first child level is a treated as a setValue() operation. Multi-path behavior allows longer paths (like alanisawesome/nickname) to be used without overwriting data. This is why the first example differs from the second example.
I am trying to use a single function createOrUpdateData(object) in my code. In case of update, it updates first level children properly, but if I have nested object passed, then it deletes all other properties of that nested object.
Here's the code:
function saveUserDetails(email,object){
var hashedEmail = Utilities.getHashCode(email);
var userRef = ref.child(hashedEmail);
return $q(function(resolve,reject){
return userRef.update(object, function(error){
if(error){
reject(error);
}else{
resolve("Updated successfully!");
}
});
});
}
So if I pass:
{
name: 'Rohan Dalvi',
externalLinks: {
website: 'mywebsite'
}
}
Then it will delete other properties inside externalLinks object. Is there a cleaner and simpler way of avoiding this?
In short, how do I make sure nested objects are only updated and that data is not deleted.
You can use multi-path updates.
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks/website": 'mywebsite'
};
userRef.update(updateObject);
By using the "externalLinks/website" syntax in the object literal it will treat the nested path as an update and not a set for the nested object. This keeps nested data from being deleted.
This question provides a more recent solution that works with cloud firestore.
Rather than using "/", one may use "." instead:
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks.website": 'mywebsite'
};
userRef.update(updateObject);
To update nested object/map/dictionary in firebase database, you can use Firestore.Encoder to class/struct that is Codable.
Here is a Swift code example:
Models:
import FirebaseFirestore
import FirebaseFirestoreSwift
// UserDetails Model
class UserDetailsModel: Codable {
let name: String,
externalLinks: ExternalLinkModel
}
// UserDetails Model
class ExternalLinkModel: Codable {
let website: String
}
Calling Firebase:
import FirebaseFirestore
import FirebaseFirestoreSwift
let firestoreEncoder = Firestore.Encoder()
let fields: [String: Any] = [
// using firestore encoder to convert object to firebase map
"externalLinks": try! firestoreEncoder.encode(externalLinkModel)
]
db.collection(path)
.document(userId)
.updateData(fields, completion: { error in
...
})
I'm using react and react-router for my application on the client side. I can't seem to figure out how to get the following query parameters from a url like:
http://xmen.database/search#/?status=APPROVED&page=1&limit=20
My routes look like this (the path is totally wrong I know):
var routes = (
<Route>
<DefaultRoute handler={SearchDisplay}/>
<Route name="search" path="?status=:status&page=:page&limit=:limit" handler={SearchDisplay}/>
<Route name="xmen" path="candidate/:accountId" handler={XmenDisplay}/>
</Route>
);
My route is working fine but I'm just not sure how to format the path to get the parameters I want. Appreciate any help on this!
Note: Copy / Pasted from comment. Be sure to like the original post!
Writing in es6 and using react 0.14.6 / react-router 2.0.0-rc5. I use this command to lookup the query params in my components:
this.props.location.query
It creates a hash of all available query params in the url.
Update:
For React-Router v4, see this answer. Basically, use this.props.location.search to get the query string and parse with the query-string package or URLSearchParams:
const params = new URLSearchParams(paramsString);
const tags = params.get('tags');
The above answers won't work in react-router v4. Here's what I did to solve the problem -
First Install query-string which will be required for parsing.
npm install -save query-string
Now in the routed component you can access the un-parsed query string like this
this.props.location.search
You can cross check it by logging in the console.
Finally parse to access the query parameters
const queryString = require('query-string');
var parsed = queryString.parse(this.props.location.search);
console.log(parsed.param); // replace param with your own
So if query is like ?hello=world
console.log(parsed.hello) will log world
OLD (pre v4):
Writing in es6 and using react 0.14.6 / react-router 2.0.0-rc5. I use this command to lookup the query params in my components:
this.props.location.query
It creates a hash of all available query params in the url.
UPDATE (React Router v4+):
this.props.location.query in React Router 4 has been removed (currently using v4.1.1) more about the issue here: https://github.com/ReactTraining/react-router/issues/4410
Looks like they want you to use your own method to parse the query params, currently using this library to fill the gap: https://github.com/sindresorhus/query-string
update 2017.12.25
"react-router-dom": "^4.2.2"
url like
BrowserHistory: http://localhost:3000/demo-7/detail/2?sort=name
HashHistory: http://localhost:3000/demo-7/#/detail/2?sort=name
with query-string dependency:
this.id = props.match.params.id;
this.searchObj = queryString.parse(props.location.search);
this.from = props.location.state.from;
console.log(this.id, this.searchObj, this.from);
results:
2 {sort: "name"} home
"react-router": "^2.4.1"
Url like http://localhost:8080/react-router01/1?name=novaline&age=26
const queryParams = this.props.location.query;
queryParams is a object contains the query params: {name: novaline, age: 26}
"react-router-dom": "^5.0.0",
you do not need to add any additional module, just in your component that has a url address like this:
http://localhost:3000/#/?authority'
you can try the following simple code:
const search =this.props.location.search;
const params = new URLSearchParams(search);
const authority = params.get('authority'); //
With stringquery Package:
import qs from "stringquery";
const obj = qs("?status=APPROVED&page=1limit=20");
// > { limit: "10", page:"1", status:"APPROVED" }
With query-string Package:
import qs from "query-string";
const obj = qs.parse(this.props.location.search);
console.log(obj.param); // { limit: "10", page:"1", status:"APPROVED" }
No Package:
const convertToObject = (url) => {
const arr = url.slice(1).split(/&|=/); // remove the "?", "&" and "="
let params = {};
for(let i = 0; i < arr.length; i += 2){
const key = arr[i], value = arr[i + 1];
params[key] = value ; // build the object = { limit: "10", page:"1", status:"APPROVED" }
}
return params;
};
const uri = this.props.location.search; // "?status=APPROVED&page=1&limit=20"
const obj = convertToObject(uri);
console.log(obj); // { limit: "10", page:"1", status:"APPROVED" }
// obj.status
// obj.page
// obj.limit
Hope that helps :)
Happy coding!
After reading the other answers (First by #duncan-finney and then by #Marrs) I set out to find the change log that explains the idiomatic react-router 2.x way of solving this. The documentation on using location (which you need for queries) in components is actually contradicted by the actual code. So if you follow their advice, you get big angry warnings like this:
Warning: [react-router] `context.location` is deprecated, please use a route component's `props.location` instead.
It turns out that you cannot have a context property called location that uses the location type. But you can use a context property called loc that uses the location type. So the solution is a small modification on their source as follows:
const RouteComponent = React.createClass({
childContextTypes: {
loc: PropTypes.location
},
getChildContext() {
return { location: this.props.location }
}
});
const ChildComponent = React.createClass({
contextTypes: {
loc: PropTypes.location
},
render() {
console.log(this.context.loc);
return(<div>this.context.loc.query</div>);
}
});
You could also pass down only the parts of the location object you want in your children get the same benefit. It didn't change the warning to change to the object type. Hope that helps.
Simple js solution:
queryStringParse = function(string) {
let parsed = {}
if(string != '') {
string = string.substring(string.indexOf('?')+1)
let p1 = string.split('&')
p1.map(function(value) {
let params = value.split('=')
parsed[params[0]] = params[1]
});
}
return parsed
}
And you can call it from anywhere using:
var params = this.queryStringParse(this.props.location.search);
Hope this helps.
"react-router-dom": "6"
I could get the value of the page property by useSearchParams
let [searchParams, setSearchParams] = useSearchParams();
searchParams.get('page')
when you have a URL like http://asd.com/report/7?page=3 you can get the page . complete example is here :
import React from 'react';
import { useSearchParams} from "react-router-dom";
const Detail = (props) => {
const [searchParams, setSearchParams] = useSearchParams();
console.log('page from search',searchParams.get('page')) // 1
return (
<div></div>
);
};
export default Detail;
reactrouter
You may get the following error while creating an optimized production build when using query-string module.
Failed to minify the code from this file:
./node_modules/query-string/index.js:8
To overcome this, kindly use the alternative module called stringquery which does the same process well without any issues while running the build.
import querySearch from "stringquery";
var query = querySearch(this.props.location.search);