Dart2 Router Implementation - dart

I am trying to upgrade Dart1 application to Dart 2.4, I am facing a problem in Router my code is as shown below
import 'dart:async';
import 'dart:convert';
import 'package:angular/src/core/di/decorators.dart';
#Injectable()
class SpRouterImpl implements SpRouter {
final Router _router;
SpRouterImpl(this._router);
#override
void go(String routeName, Map<String, String> parameters,
[bool openInNewWindow = false]) {
if (openInNewWindow) {
var url = _router.generate([routeName, parameters]).component.urlPath;
window.open(url, "_blank");
} else {
_router.navigate([routeName, parameters]);
}
}
}
I am getting error in this line
var url = _router.generate([routeName, parameters]).component.urlPath;
The method generate isn't defined for the class Router
Second error is here
_router.navigate([routeName, parameters]);
The argument type List can't be assigned to the parameter type 'String'
The above function is working fine in Dart 1 but when I upgrade to Dart 2, I am getting the errors, don't know how to solve it.
Can anyone help in this regard

You need a RoutePath instance to define your "route".
final search = RoutePath(path: "search/:term"); // term is the parameter
Then use that path to navigate to that route.
_router.navigate(search.toUrl(parameters: {'term': searchTerm}));
So in your case it might look like this:
import 'dart:async';
import 'dart:convert';
import 'package:angular/src/core/di/decorators.dart';
#Injectable()
class SpRouterImpl implements SpRouter {
final Router _router;
SpRouterImpl(this._router);
#override
void go(String routeName, Map<String, String> parameters,
[bool openInNewWindow = false]) {
final path = RoutePath(routeName);
final url = path.toUrl(parameters: parameters)
if (openInNewWindow) {
window.open(url, "_blank");
} else {
_router.navigate(url);
}
}
}
It might not drop in and work depending on how your routeName is defined but this is the general idea.
There are several other options for RoutePath check them out and see what works best for you!

Related

How to specify Future type correctly

I'm trying to figure out how to specify types in code like this:
import 'dart:convert';
import 'package:http/http.dart' as http;
main() async {
Map repos = await(fetchJson());
print(repos['name']);
}
Object fetchJson() async {
final uri = 'https://api.github.com/users/sjindel-google/repos';
final response = await http.get(uri);
return jsonDecode(response.body)[1];
}
That code works, but I want to specify the type for fetchJson(). It seems like it should be:
Future<Map> fetchJson() async {
But that gives me the runtime error:
Error: 'Future' expects 0 type arguments.
What's the right way to do this?

Localize a widget in Flutter

I'm currently test driving and I have already built a localized "Hello world" following the tutorial. However, when I tried to move my widget to a different file, I got a red screen of death with the error:
The following NoSuchMethodError was thrown building ToDoItem(dirty):
The method 'helloWorld' was called on null.
Receiver: null
Tried calling: helloWorld()
My To Do item class looks like this
todo_item.dart
import 'package:flutter/material.dart';
import 'package:to_do_app/localization.dart';
class ToDoItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Text(ToDoLocalizations.of(context).helloWorld());
}
}
Of course the problem is that my Localization class has not been initialized, however I don't know how to initialize it since the Text widget does not have a LocalizationDelegates parameter.
I'm aware that this could be fixed by injecting the String directly into my widget's constructor, but for the sake of it I want to know how to localize widgets.
EDIT: Here is my localization class
import 'dart:async';
import 'dart:developer';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:to_do_app/l10n/messages_all.dart';
class ToDoLocalizations {
ToDoLocalizations(Locale locale) : _localeName = locale.toString();
final String _localeName;
static Future<ToDoLocalizations> load(Locale locale) {
return initializeMessages(locale.toString()).then((Object _) {
return new ToDoLocalizations(locale);
});
}
static ToDoLocalizations of(BuildContext context) {
return Localizations.of<ToDoLocalizations>(context, ToDoLocalizations);
}
String helloWorld() {
return Intl.message(
'Hello, World!',
name: 'helloWorld',
desc: 'A friendly salutation',
locale: _localeName
);
}
}
class ToDoLocalizationsDelegate
extends LocalizationsDelegate<ToDoLocalizations> {
#override
bool isSupported(Locale locale) {
return ['en', 'nb'].contains(locale.languageCode);
}
#override
bool shouldReload(LocalizationsDelegate<ToDoLocalizations> old) {
return false;
}
#override
Future<ToDoLocalizations> load(Locale locale) {
return ToDoLocalizations.load(locale);
}
}
After closely following the aforementioned tutorial and the code outlined on this example repository without success, this answer pointed me in the right direction. All of my code was sound and the only missing part was to write import statements using relative paths instead of absolute paths and then the error disappeared.
I had added the MaterialApp widget in main.dart, further again in some of files i added MaterialApp by accident. So, adding the second MaterialApp widget overrode what I had previously defined.

Naming a method `get` causes error

See this simple class and method:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart';
import 'package:angular/angular.dart';
#Injectable()
class ApiClient {
final Client _http;
static final _headers = { 'Content-Type': 'application/json' };
static final _encodedHeaders = { 'Content-Type': 'application/x-www-form-urlencoded' };
ApiClient(this._http);
Future<T> get<T>(String url, T f(dynamic e)) async {
try {
final response = await _http.get(url);
var data = JSON.decode(response.body);
print(data);
if(data == null)return null;
final ts = f(data);
return ts;
} catch (e) {
_handleError(e);
return null;
}
}
}
It causes this error:
Unexpected token 'Future'.
Future get(String url, T f(dynamic e)) async {
^^^^^^
and when I rename the method say get1 the error goes away. Is this normal? I have other classes with method named get and works just fine. Am I missing something here?
See the issue on github
UPDATE:
It doesn't seem to me to be an identifier issue. Cause I can name a method get and there wouldn't be any error. Also, there is some built-in classes which have methods named get (e.g. Client class). It seems naming a generic-method to get causes the error: get<T>(). I agree with Vyacheslav Egorov - as said in comment, and I think this is a parser bug.
get is a built-in identifier to define a getter and should not be used as identifier
https://www.dartlang.org/guides/language/language-tour

Posting to a REST API on form submit with Orbeon

I am looking through the documentation for a sample of how to handle a submit from an Orbeon form that I gather some data in and then submitting to another application via REST. I am not seeing anything that shows how to do that. Does Orbeon provide functionality to do that or do I need to code some JSP or something else on the backside to handle that?
My understanding is, that you have to provide/implement the REST service yourself. You aren't restricted to do it in Java, but if this is your preferred language, here's how a very simple servlet would look like. In this case the REST service saves the form in a file in the temp directory.
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Optional;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FormDumpServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(FormDumpServlet.class.getName());
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
protected Optional<String> makeTempDir() {
final String dir = System.getProperty("java.io.tmpdir");
logger.info(String.format("java.io.tmpdir=%s", dir));
if (dir == null) {
logger.severe("java.io.tmpdir is null, can't create temp directory");
return Optional.empty();
}
final File f = new File(dir,"form-dumps");
if (f.exists() && f.isDirectory() && f.canWrite()) {
return Optional.of(f.getAbsolutePath());
}
if (f.mkdir()) {
return Optional.of(f.getAbsolutePath());
}
logger.severe(String.format("failed to create temp dir <%s>", f.getAbsolutePath()));
return Optional.empty();
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getPathInfo();
if (!path.equalsIgnoreCase("/accept-form")) {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
Enumeration<String> parameterNames = req.getParameterNames();
while(parameterNames.hasMoreElements()) {
final String name = parameterNames.nextElement();
final String value = req.getParameter(name);
logger.info(String.format("parameter: name=<%s>, value=<%s>", name, value));
}
Optional<String> tempPath = makeTempDir();
if (tempPath.isPresent()) {
String fn = String.format("%s.xml", FORMAT.format(new Date()));
File f = new File(new File(tempPath.get()), fn);
logger.info(String.format("saving form to file <%s>", f.getAbsolutePath()));
try(PrintWriter pw = new PrintWriter(new FileWriter(f))) {
req.getReader().lines().forEach((l) -> pw.println(l));
}
}
resp.setStatus(HttpServletResponse.SC_OK);
}
}
You also have to configure a property in properties-local.xml which connects the send action for your form (the form with the name my_form in your application my_application) to the REST endpoint. This property could look as follows:
<property
as="xs:string"
name="oxf.fr.detail.process.send.my_application.my_form"
>
require-valid
then save-final
then send(uri = "http://localhost:8080/my-form-dump-servlet/accept-form")
then success-message(message = "Success: the form was transferred to the REST service")
</property>

How can I test HMAC authentication using Dropwizard?

I'm just getting started with Dropwizard 0.4.0, and I would like some help with HMAC authentication. Has anybody got any advice?
Thank you in advance.
At present Dropwizard doesn't support HMAC authentication right out of the box, so you'd have to write your own authenticator. A typical choice for HMAC authentication is to use the HTTP Authorization header. The following code expects this header in the following format:
Authorization: <algorithm> <apiKey> <digest>
An example would be
Authorization: HmacSHA1 abcd-efgh-1234 sdafkljlkansdaflk2354jlkj5345345dflkmsdf
The digest is built from the content of the body (marshalled entity) prior to URL encoding with the HMAC shared secret appended as base64. For a non-body request, such as GET or HEAD, the content is taken as the complete URI path and parameters with the secret key appended.
To implement this in a way that Dropwizard can work with it requires you to copy the BasicAuthenticator code present in the dropwizard-auth module into your own code and modify it with something like this:
import com.google.common.base.Optional;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.yammer.dropwizard.auth.AuthenticationException;
import com.yammer.dropwizard.auth.Authenticator;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
class HmacAuthInjectable<T> extends AbstractHttpContextInjectable<T> {
private static final String PREFIX = "HmacSHA1";
private static final String HEADER_VALUE = PREFIX + " realm=\"%s\"";
private final Authenticator<HmacCredentials, T> authenticator;
private final String realm;
private final boolean required;
HmacAuthInjectable(Authenticator<HmacCredentials, T> authenticator, String realm, boolean required) {
this.authenticator = authenticator;
this.realm = realm;
this.required = required;
}
public Authenticator<HmacCredentials, T> getAuthenticator() {
return authenticator;
}
public String getRealm() {
return realm;
}
public boolean isRequired() {
return required;
}
#Override
public T getValue(HttpContext c) {
try {
final String header = c.getRequest().getHeaderValue(HttpHeaders.AUTHORIZATION);
if (header != null) {
final String[] authTokens = header.split(" ");
if (authTokens.length != 3) {
// Malformed
HmacAuthProvider.LOG.debug("Error decoding credentials (length is {})", authTokens.length);
throw new WebApplicationException(Response.Status.BAD_REQUEST);
}
final String algorithm = authTokens[0];
final String apiKey = authTokens[1];
final String signature = authTokens[2];
final String contents;
// Determine which part of the request will be used for the content
final String method = c.getRequest().getMethod().toUpperCase();
if ("GET".equals(method) ||
"HEAD".equals(method) ||
"DELETE".equals(method)) {
// No entity so use the URI
contents = c.getRequest().getRequestUri().toString();
} else {
// Potentially have an entity (even in OPTIONS) so use that
contents = c.getRequest().getEntity(String.class);
}
final HmacCredentials credentials = new HmacCredentials(algorithm, apiKey, signature, contents);
final Optional<T> result = authenticator.authenticate(credentials);
if (result.isPresent()) {
return result.get();
}
}
} catch (IllegalArgumentException e) {
HmacAuthProvider.LOG.debug(e, "Error decoding credentials");
} catch (AuthenticationException e) {
HmacAuthProvider.LOG.warn(e, "Error authenticating credentials");
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
if (required) {
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.AUTHORIZATION,
String.format(HEADER_VALUE, realm))
.entity("Credentials are required to access this resource.")
.type(MediaType.TEXT_PLAIN_TYPE)
.build());
}
return null;
}
}
The above is not perfect, but it'll get you started. You may want to refer to the MultiBit Merchant release candidate source code (MIT license) for a more up to date version and the various supporting classes.
The next step is to integrate the authentication process into your ResourceTest subclass. Unfortunately, Dropwizard doesn't provide a good entry point for authentication providers in v0.4.0, so you may want to introduce your own base class, similar to this:
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.test.framework.AppDescriptor;
import com.sun.jersey.test.framework.JerseyTest;
import com.sun.jersey.test.framework.LowLevelAppDescriptor;
import com.xeiam.xchange.utils.CryptoUtils;
import com.yammer.dropwizard.bundles.JavaBundle;
import com.yammer.dropwizard.jersey.DropwizardResourceConfig;
import com.yammer.dropwizard.jersey.JacksonMessageBodyProvider;
import com.yammer.dropwizard.json.Json;
import org.codehaus.jackson.map.Module;
import org.junit.After;
import org.junit.Before;
import org.multibit.mbm.auth.hmac.HmacAuthProvider;
import org.multibit.mbm.auth.hmac.HmacAuthenticator;
import org.multibit.mbm.persistence.dao.UserDao;
import org.multibit.mbm.persistence.dto.User;
import org.multibit.mbm.persistence.dto.UserBuilder;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Set;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* A base test class for testing Dropwizard resources.
*/
public abstract class BaseResourceTest {
private final Set<Object> singletons = Sets.newHashSet();
private final Set<Object> providers = Sets.newHashSet();
private final List<Module> modules = Lists.newArrayList();
private JerseyTest test;
protected abstract void setUpResources() throws Exception;
protected void addResource(Object resource) {
singletons.add(resource);
}
public void addProvider(Object provider) {
providers.add(provider);
}
protected void addJacksonModule(Module module) {
modules.add(module);
}
protected Json getJson() {
return new Json();
}
protected Client client() {
return test.client();
}
#Before
public void setUpJersey() throws Exception {
setUpResources();
this.test = new JerseyTest() {
#Override
protected AppDescriptor configure() {
final DropwizardResourceConfig config = new DropwizardResourceConfig();
for (Object provider : JavaBundle.DEFAULT_PROVIDERS) { // sorry, Scala folks
config.getSingletons().add(provider);
}
for (Object provider : providers) {
config.getSingletons().add(provider);
}
Json json = getJson();
for (Module module : modules) {
json.registerModule(module);
}
config.getSingletons().add(new JacksonMessageBodyProvider(json));
config.getSingletons().addAll(singletons);
return new LowLevelAppDescriptor.Builder(config).build();
}
};
test.setUp();
}
#After
public void tearDownJersey() throws Exception {
if (test != null) {
test.tearDown();
}
}
/**
* #param contents The content to sign with the default HMAC process (POST body, GET resource path)
* #return
*/
protected String buildHmacAuthorization(String contents, String apiKey, String secretKey) throws UnsupportedEncodingException, GeneralSecurityException {
return String.format("HmacSHA1 %s %s",apiKey, CryptoUtils.computeSignature("HmacSHA1", contents, secretKey));
}
protected void setUpAuthenticator() {
User user = UserBuilder
.getInstance()
.setUUID("abc123")
.setSecretKey("def456")
.build();
//
UserDao userDao = mock(UserDao.class);
when(userDao.getUserByUUID("abc123")).thenReturn(user);
HmacAuthenticator authenticator = new HmacAuthenticator();
authenticator.setUserDao(userDao);
addProvider(new HmacAuthProvider<User>(authenticator, "REST"));
}
}
Again, the above code is not perfect, but the idea is to allow a mocked up UserDao to provide a standard user with a known shared secret key. You'd have to introduce your own UserBuilder implementation for testing purposes.
Finally, with the above code a Dropwizard Resource that had an endpoint like this:
import com.google.common.base.Optional;
import com.yammer.dropwizard.auth.Auth;
import com.yammer.metrics.annotation.Timed;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.persistence.dto.User;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.atomic.AtomicLong;
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
private final String template;
private final String defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = defaultName;
this.counter = new AtomicLong();
}
#GET
#Timed
#Path("/hello-world")
public Saying sayHello(#QueryParam("name") Optional<String> name) {
return new Saying(counter.incrementAndGet(),
String.format(template, name.or(defaultName)));
}
#GET
#Timed
#Path("/secret")
public Saying saySecuredHello(#Auth User user) {
return new Saying(counter.incrementAndGet(),
"You cracked the code!");
}
}
could be tested with a unit test that was configured like this:
import org.junit.Test;
import org.multibit.mbm.core.Saying;
import org.multibit.mbm.test.BaseResourceTest;
import javax.ws.rs.core.HttpHeaders;
import static org.junit.Assert.assertEquals;
public class HelloWorldResourceTest extends BaseResourceTest {
#Override
protected void setUpResources() {
addResource(new HelloWorldResource("Hello, %s!","Stranger"));
setUpAuthenticator();
}
#Test
public void simpleResourceTest() throws Exception {
Saying expectedSaying = new Saying(1,"Hello, Stranger!");
Saying actualSaying = client()
.resource("/hello-world")
.get(Saying.class);
assertEquals("GET hello-world returns a default",expectedSaying.getContent(),actualSaying.getContent());
}
#Test
public void hmacResourceTest() throws Exception {
String authorization = buildHmacAuthorization("/secret", "abc123", "def456");
Saying actual = client()
.resource("/secret")
.header(HttpHeaders.AUTHORIZATION, authorization)
.get(Saying.class);
assertEquals("GET secret returns unauthorized","You cracked the code!", actual.getContent());
}
}
Hope this helps you get started.

Resources