Type graphql - set createUnionType as InputType - typegraphql

I'm using type-graphql framework and I've configured a union type like so:
export const ActionUnion = createUnionType({
name: 'ActionUnion',
types: () => [CreateAction, UpdateAction, DeleteAction],
});
And I want to use it as my mutation input.
But when I run the service I get an error saying:
Cannot determine GraphQL input type for userActions
Is there a way to configure unionType to be InputType as well?
Thanks in advance.

No, in GraphQL spec, unions are only for object types. You can't have union input.

Related

Loopback 4: Authorization decoration of CRUDRestController - is it in anyway possible?

Wondering if anybody in the community has any experience or guidance on how one could use
Authorization decorators (or any custom decoration?)(https://loopback.io/doc/en/lb4/Decorators_authorize.html) on CrudRestController endpoints? (https://loopback.io/doc/en/lb4/Creating-crud-rest-apis.html).
Looked at the src for crud-rest.controller.ts and it just seems like there is no way to really do it.
It seems like it's not easily possible to use any decoration of endpoints in a CrudRestController without taking a very hacky approach and/or wholesale duplicating the code in crud-rest.controller.ts and that we'll have to basically write every endpoint for every model by hand.
Maybe someone has come up with something or has some guidance on an approach? Is the only way to use auth with CrudRestController with the AuthorizationComponent as of now to use Authorizer functions (https://loopback.io/doc/en/lb4/Authorization-component-authorizer.html)
Seems like one part lies in this :
https://github.com/loopbackio/loopback4-example-shopping/blob/9188104c01516a5cbd4ce13f28abe18bafef821e/packages/shopping/src/services/basic.authorizor.ts
/**
* Allow access only to model owners, using route as source of truth
*
* eg. #post('/users/{userId}/orders', ...) returns `userId` as args[0]
*/
if (currentUser[securityId] === authorizationCtx.invocationContext.args[0]) {
return AuthorizationDecision.ALLOW;
}
So I ended up doing :
async authorize(
context: AuthorizationContext,
metadata: AuthorizationMetadata,
) {
const parent = context.invocationContext?.parent
const request = parent?.getBinding("rest.http.request").getValue(parent)
const givenUserId = request?.body?.userId
// next line finds out the user id in the JWT payload
const jwtUserId = context?.principals[0]?.payload?.sub
if (!jwtUserId || (givenUserId && givenUserId != jwtUserId)) {
return AuthorizationDecision.DENY;
} else {
return AuthorizationDecision.ALLOW;
}
}
as my userId is provided in the http parameters (post form or get parameters)
I also use a custom JTWService to read the payload and make it available in the UserProfile.
This may not be the best way to do it, but so far it works. I am still working on finding out how to deal with read requests and add a filter on all of them by userId too using decorators I will post my finding here, if nothing better show up first here.

Api Platform pagination custom page_parameter_name

I have very specific question on which I cannot find any answer and/or solution provided for Api Platform.
By default, the documentation states, that if you want to pass a page parameter for paging action, you must do the following:
pagination:
page_parameter_name: _page
However, due to the nature of our frontend we're not able to pass this variable to the request. It is hardcoded to the frontend request and is something like page[number]=1.
Is it possible to configure page_parameter_name to receive this variable or we need to transform it somehow in the Api itself?
Thank you!
ApiPlatform\Core\EventListener\ReadListener::onKernelRequest gets $context['filters'] from the request through ApiPlatform\Core\Util\RequestParser::parseRequestParams which ultimately uses PHP's parse_str function so the value of 'page[number]' will be in $context$context['filters']['page']['number'].
ApiPlatform\Core\DataProvider\Pagination::getPage retrieves the page number from $context['filters'][$parameterName] so whatever the value of [$parameterName] it will at best retrieve the array ['number'=> 1].
Then ::getPage casts that to int, which happens to be 1. But will (at least with PHP7) be 1 for any value under 'number'.
Conclusion: You need to transform it somehow in the Api itself. For example by decoration of the ApiPlatform\Core\DataProvider\Pagination service (api_platform.pagination).
API_URL?page[number]=2
print_r($request->attributes->get('_api_pagination'));
Array(
[number] => 2
)
The value of the "page_parameter_name" parameter should be "number" .
api_platform.yaml
collection:
pagination:
page_parameter_name: number
This may not work in version 3
vendor/api-platform/core/src/JsonApi/EventListener/TransformPaginationParametersListener.php
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
$pageParameter = $request->query->all()['page'] ?? null;
...
/* #TODO remove the `_api_pagination` attribute in 3.0 */
$request->attributes->set('_api_pagination', $pageParameter);
}

Parse-server result.attributes vs get()

Been using Parse Server for about a month in our project, and it's been a fantastic addition to our tech stack. However, there is one thing that has slowed down our team a bit ; when using Parse.Query, we can only access the fields of the returned Object by using get('fieldName'), which seems to be very redundant and prone to errors (using strings to get the fields). In Firebase, there is a method to get all the data : .data(). I haven't seen this feature in Parse.
We found out about a property when getting the query result called attributes. It seems to be an object that we can destructure and directly get all the fields of the Parse Object. For example :
const query = Parse.Query('Movie');
const result = await query.first();
const { title, price } = result.attributes
There is only a slight reference to it in the docs : https://parseplatform.org/Parse-SDK-JS/api/master/Parse.Object.html under Members, only with the description Prototype getters/setters.
If this property makes it much more easier/convenient than the get() method, is there any reason it is not include in the get started guide of Parse-SDK-JS? Or am I missing something?
Thanks
As you can see in this line, the get method just access the attributes property and returns the value for the specified key, so you should be fine with const { title, price } = result.attributes.

mapping response of axios call to model

I am doing a simple axios call and getting few values. I want them to convert to a model. the problem is the server side uses different case [snakecase] and has underscores.. but in typescript model , it doesnt have anything..
How can i convert those response values to this model values?how to map them..
async getStudentstatus (): Promise<AxiosResponse<StudentStatus>> {
return Axios.get(`url`)
}
so the StudentStatus is like this
export interface StudentStatus {
failedInAllExams: boolean,
attendedAllExams: boolean,
totalScore: number
}
but server side has value
{"failed_in_all_exams":true,"attended_all_exams":"true","totalScore":"290"}
I am looking manually do it ;not use a library since this is a one of a case
I would use something like this https://www.npmjs.com/package/camelcase to convert data keys to camel case. I believe simple key conversion should cover 99% of possible cases.

Express routes parameters

I am trying to create two routes in my express app. One route, without a parameter will give me a list of choices, the other one with a parameter will give me the choice related to the id.
router.get('/api/choice', choice_controller.get_choices);
router.get('/api/choice/:id', choice_controller.get_choice);
When I go to .../api/choice/?id=1 the api returns the list of choices, and therefore follows the route without the param (/api/choice). How do I make sure that the router does not omit the parameter?
Thanks in advance.
UPDATE
It seems that it does not fire the /api/choice/:id route. If I remove the one without the param, it gives a 404 so. Could someone explain to me why /api/choice/?id=1 is not getting picked up by /api/choice/:id?
Basically, your declared routes are documented in the Express documentation.
The second route is resolved by a URL like /api/choice/hello where 'hello' is mapped into the req object object as:
router.get('/api/choice/:id', function (req, res) {
console.log("choice id is " + req.params.id);
});
What you are actually trying is mapping query parameters.
A URL like /api/choice/?id=1 is resolved by the first router you provided.
Query parameters are easy to get mapped against the request as:
router.get('/api/choice', function (req, res) {
console.log('id: ' + req.query.id);
//get the whole query as!
const queryStuff = JSON.stringify(req.query);
console.log(queryStuff)
});