How to split a RESTDataSource class into multiple files - apollo

We have an issue with my team where a RESTDataSource class has many methods and it makes the file 1500 lines long.
This class can be split into multiple themes, for instance a Facebook datasource has some methods about the user, some methods about the article...
Has anyone a method to split this class so that it becomes less messy?
For info this is the class I am talking about (but 1500 lines long) :
const { RESTDataSource } = require('apollo-datasource-rest');
class MoviesAPI extends RESTDataSource {
constructor() {
// Always call super()
super();
// Sets the base URL for the REST API
this.baseURL = 'https://movies-api.example.com/';
}
async getMovie(id) {
// Send a GET request to the specified endpoint
return this.get(`movies/${encodeURIComponent(id)}`);
}
async getMostViewedMovies(limit = 10) {
const data = await this.get('movies', {
// Query parameters
per_page: limit,
order_by: 'most_viewed',
});
return data.results;
}
}

Related

How to use Mockito to verify stream callback

I developing an application an flutter and use clean architecture.
I created a use case return a List from a stream. The stream sends the List from an observer. Above is the code:
abstract class GetAllServicesObserver implements Observer {
void onGetAllSuccess(List<Service> services);
void onGetAllError(Exception error);
}
class GetAllServices extends UseCase<GetAllServicesObserver, NoParams> {
final User _user;
final ServiceRepository _serviceRepository;
StreamSubscription _subscription;
GetAllServices({
#required User user,
#required ServiceRepository serviceRepository,
}) : _user = user,
_serviceRepository = serviceRepository;
#override
action(observer, params) async {
_subscription?.cancel();
final _stream = _serviceRepository.all(_user);
_subscription = _stream.listen((services) {
observer.onGetAllSuccess(services);
}, onError: (e) {
observer.onGetAllError(e);
});
}
}
And I created an unit test to this use case:
test('should to return all services', () {
//setup
when(repository.all(user)).thenAnswer((_) async* {
yield List<Service>();
});
final useCase = GetAllServices(user: user, serviceRepository: repository);
useCase.observer = observer;
//run
useCase();
//verify
verify(observer.onGetAllSuccess(List<Service>()));
});
}
But it's returns the follow message and not pass:
ERROR: No matching calls (actually, no calls at all).
(If you called verify(...).called(0);, please instead use verifyNever(...);.)
Would anyone know what the problem is?
Have you tried untilCalled before verify? e.g.:
await untilCalled(some method that will be called)

Adonis JS - Access Model's custom method

I've added a custom method called customMethod to a model, like so:
class Prize extends Model {
customMethod(){
return 'test'
}
}
When I use find to get a prize by primary key I can call this method no problem
const prize = await Prize.find(1);
return prize.customMethod();
//returns 'test'
but when if I get a prize any other way, through a relationship or by querying by a field, I can't access this method.
const countrysPrizes = await country.prizes().fetch();
for (const key in countrysPrizes) {
if (countrysPrizes.hasOwnProperty(key)) {
const prize = countrysPrizes[key];
return prize.customMethod();
//returns 500 - prize.customMethod is not a function
}
}
How can I access this method while iterating through multiple of the object?
You need to use <fetched_object>.rows because of VanillaSerializer
Example code :
const user = await User.find(1);
const posts = await user.posts().fetch();
posts.rows.forEach(post=> { // use .rows
console.info(post.customMethod()); // Your custom method
});
I've had trouble using the basic foreach loop. So I used .foreach()
An output example with fetch() :

Adonis.js - Seeding Users and Profiles throwing DB error (Postgres)

I am trying to write a seed file for users and profiles with a one to one relationship and currently getting an "error: relation 'user_profiles' does not exist". From digging around, it seems like Adonis will assume this as a pivot table in the case of a many to many relationship. What I (think or intend to) have is a one to one relationship between users and profiles. Thanks in advance! Newbie to SQL and Adonis. As a side note, the user persists to the db, but there is no corresponding profile.
// My User Schema
class UserSchema extends Schema {
up () {
this.create('users', (table) => {
table.increments('id')
table.string('username', 80).notNullable().unique()
table.string('email', 254).notNullable().unique()
table.string('password', 60).notNullable()
// table.integer('profile_id').unsigned().references('id').inTable('userprofiles')
table.timestamps()
})
}
down () {
this.drop('users')
}
}
// My Profile Schema
class UserprofileSchema extends Schema {
up () {
this.create('userprofiles', (table) => {
table.increments()
table.string('first_name')
table.string('last_name')
// table.integer('user_id')
// .unsigned().references('id').inTable('users')
table.integer('user_id')
.unsigned()
.index('user_id')
table.foreign('user_id')
.references('users.id')
table.timestamps()
})
}
down () {
this.drop('userprofiles')
}
}
My User model includes the following relationship definition:
profile () {
return this.hasOne('App/Models/UserProfile')
}
// Seed script
class UserSeeder {
async run () {
try {
const user = await Factory.model('App/Models/User').create()
const userProfile = await Factory.model('App/Models/UserProfile').make()
userProfile.user_id = user.id
await user.profile().save(userProfile)
} catch (e) {
console.log('Error From Seeder: ', e);
}
}
}
Error code '42P01' and can post whole body if needed. Thanks!
On your Model userProfile, set table name as follows.
class User extends Model {
static get table () {
return 'userprofiles'
}
}

Angular2 ASP.NET Core AntiForgeryToken

I have an Angular2 app. It is running within ASP.NET 5 (Core).
It makes Http calls to the controller which is working fine.
But now I need to establish Cross Site Scripting projection.
How do I generate a new token on each Http request and then subsequently perform the AntiForgeryToken check in Angular2 apps?
Note: My data forms in Angular are not produced from an MVC view but entirely written in Angular2 and call web services only.
All the examples I have seen are out dated and do not work / do not work fully.
How do I integrate AntiForgeryToken checks in Angular2 against ASP.NET 5 where forms are pure Angular?
Thanks.
A custom action filter is not necessary. It can all be wired up in Startup.cs.
using Microsoft.AspNetCore.Antiforgery;
(...)
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
(...)
}
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
app.Use(next => context =>
{
if (context.Request.Path == "/")
{
//send the request token as a JavaScript-readable cookie, and Angular will use it by default
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
}
return next(context);
});
(...)
}
Then all you need in your controllers is the [ValidateAntiForgeryToken] decorator wherever you want to enforce that a token is provided.
For reference, I found this solution here - AspNet AntiForgery Github Issue 29.
I am using a action filter to send the request tokens.
Simply apply it to the actions you want a new antiforgery token, e.g. Angular2 SPA, WebAPI action, etc.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class AngularAntiForgeryTokenAttribute : ActionFilterAttribute
{
private const string CookieName = "XSRF-TOKEN";
private readonly IAntiforgery antiforgery;
public AngularAntiForgeryTokenAttribute(IAntiforgery antiforgery)
{
this.antiforgery = antiforgery;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
if (!context.Cancel)
{
var tokens = antiforgery.GetAndStoreTokens(context.HttpContext);
context.HttpContext.Response.Cookies.Append(
CookieName,
tokens.RequestToken,
new CookieOptions { HttpOnly = false });
}
}
}
/* HomeController */
[ServiceFilter(typeof(AngularAntiForgeryTokenAttribute), IsReusable = true)]
public IActionResult Index()
{
return View();
}
/* AccountController */
[HttpPost()]
[AllowAnonymous]
[ValidateAntiForgeryToken]
// Send new antiforgery token
[ServiceFilter(typeof(AngularAntiForgeryTokenAttribute), IsReusable = true)]
public async Task<IActionResult> Register([FromBody] RegisterViewModel model)
{
//...
return Json(new { });
}
Register the attribute in Startup, and configure Antiforgery service to read the request token form "X-XSRF-TOKEN" header.
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<AngularAntiForgeryTokenAttribute>();
services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});
}
}
I think you need to make custom AntiForgeryValidationToken attribute that supports sending token via header instead of form values. Then add token to header of every request from your Angular2 app to your api. Example here How do you set global custom headers in Angular2?
To validate the token from a header you can use something like this:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ValidateHeaderAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException(nameof(filterContext));
}
var httpContext = filterContext.HttpContext;
if (httpContext.Request.Headers["__RequestVerificationToken"] == null)
{
httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
httpContext.Response.StatusDescription = "RequestVerificationToken missing.";
filterContext.Result = new JsonResult
{
Data = new { ErrorMessage = httpContext.Response.StatusDescription },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
return;
}
var cookie = httpContext.Request.Cookies[System.Web.Helpers.AntiForgeryConfig.CookieName];
System.Web.Helpers.AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
}
}
Then you just add [ValidateHeaderAntiForgeryToken] on the methods in your controller. Note though, this is from a MVC 5, ASP.NET 4.5.2 project, so you may have to alter it slightly to adjust to .NET Core. Also I modified this to return a JSON result if the token is missing, you can remove that part if you don't handle the error response and output it to the user.
Credits for the core part of this attribute goes to: https://nozzlegear.com/blog/send-and-validate-an-asp-net-antiforgerytoken-as-a-request-header
The hard part is how to generate the AntiForgeryToken without using #Html.AntiForgeryToken() in pure Angular 2 application (without access to .cshtml files). I'm looking for an answer to that as well.

JSON rendering fails for domain objects in ControllerUnitTestCase

I'm writing a unit test for a Grails controller that renders a domain class to a JSON response:
class MyController {
def find = {
def domainInst = MyDomainClass.get(params.id)
render ([data: domainInst] as JSON)
}
}
The unit test extends ControllerUnitTestCase and provides a mock for the domain object:
class MyControllerTests extends ControllerUnitTestCase {
#Before
void setUp() {
super.setUp()
mockDomain(MyDomainClass, [new MyDomainClass(id: 7)])
}
#Test
void testFind() {
def inst = MyDomainClass.get(7)
controller.params.id = inst.id
controller.find()
assert(controller.response.json.data.id == inst.id)
}
This all seems to be working nicely except for the JSON rendering, which spits out a nasty stack trace:
| Failure: testFind(MyControllerTests)
| org.apache.commons.lang.UnhandledException:
org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
Caused by: org.codehaus.groovy.grails.web.converters.exceptions.ConverterException: Error converting Bean with class MyDomainClass
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.convertAnother(JSON.java:162)
at grails.converters.JSON.value(JSON.java:199)
at grails.converters.JSON.render(JSON.java:134)
... 5 more
Caused by: java.lang.reflect.InvocationTargetException
... 9 more
Caused by: groovy.lang.MissingMethodException: No signature of method: MyDomainClass.isAttached() is applicable for argument types: () values: []
Possible solutions: isAttached(), attach()
... 9 more
Changing the return to a Map instead of a domain class works:
render ([data: [id: domainInst.id]] as JSON)
What's causing the JSON marshaller to die on the domain class? It works in a normal environment, but not in the mock test environment. Is there a way to make this test work?
Looks like you might need to do some fine tuning to make the converters realize that you're trying to render a domain class as a JSON object. It works when you manually put your id into a map because it is rendering the response from a Map object instead of a Grails domain class, which needs to go through a special ObjectMarshaller.
Something like this:
// Domain Class
class Foo {
String foo
}
// Controller class
class MyController {
def find = {
def domainInst = Foo.get(params.id)
render domainInst as JSON
}
}
// Controller Test Class
class MyControllerTests extends ControllerUnitTestCase {
static application
#Before
void setUp() {
super.setUp()
// Register some common classes so that they can be converted to XML, JSON, etc.
def convertersInit = new ConvertersConfigurationInitializer()
convertersInit.initialize(application)
[ List, Set, Map, Errors ].each { addConverters(it) }
def xmlErrorMarshaller = new ValidationErrorsMarshaller()
XML.registerObjectMarshaller(xmlErrorMarshaller)
def jsonErrorMarshaller = new ValidationErrorsMarshaller()
JSON.registerObjectMarshaller(jsonErrorMarshaller)
ApplicationHolder.application.addArtefact("Domain", Foo)
mockDomain(Foo, [new Foo(foo: "foo")] )
}
#Test
void testJSON() {
def inst = Foo.list()[0]
controller.params.id = inst.id
def model = controller.find()
assert controller.response.json.foo == "foo"
}
#Override
protected def bindMockWebRequest(GrailsMockHttpServletRequest mockRequest, GrailsMockHttpServletResponse mockResponse) {
MockApplicationContext ctx = new MockApplicationContext()
application = new DefaultGrailsApplication([testClass] as Class[], getClass().classLoader)
application.initialise()
ctx.registerMockBean("grailsApplication", application)
ctx.registerMockBean(testClass.name, testClass.newInstance())
def lookup = new TagLibraryLookup(applicationContext: ctx, grailsApplication: application)
lookup.afterPropertiesSet()
ctx.registerMockBean("gspTagLibraryLookup", lookup)
ctx.registerMockBean(GroovyPagesUriService.BEAN_ID, new DefaultGroovyPagesUriService())
mockRequest.servletContext.setAttribute(ApplicationAttributes.APPLICATION_CONTEXT, ctx)
mockRequest.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx)
webRequest = new GrailsWebRequest(mockRequest, mockResponse, mockRequest.servletContext)
mockRequest.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
}
Hope this helps!