I have a map, named finalMap;
var finalMap = Map<String, String>();
It holds question id and answer.
The output is {17: W, 19: N, 83: yes}
Also I have a answerTable list, named answerTable (List)
[ChecklistAnswerItem(id: 142, sectionItemId: 17, checklistsectionItem: Instance of 'ChecklistAnswerSectionItem', inputAnswer: W)]
What I trying to achieve is if the map key is exits in answerTable.sectionItemId, it call edit api, otherwise call create api.
Future editChecklistAnswer(
String id, String checklistAnswerId, Map<String, String> textMap) async {
List<ChecklistAnswerItem> answerTable =
await _repository.selectChecklistAnswerItemsList();
..
print(finalMap);
print(answerTable);
for (final entry in finalMap.entries) {
if (answerTable.isEmpty) {
print("submit" + entry.key.toString());
// I have my create api here
} else {
for (var i in answerTable) {
if (entry.key.toString() == i.sectionItemId.toString()) {
print("edit " + entry.key.toString());
// I have edit api here
} else {
print("create " + entry.key.toString());
// I have create api here
}
}
}
}
}
But I get a weird result. At first I can call the create api when answerTable length is empty. But when I go to edit it, it will call create api after edit api called..What the issue here?
Output
{17: T, 16: v}
[]
submit 17
submit 16
{17: T, 16: 123}
[ChecklistAnswerItem(id: 388, sectionItemId: 17, checklistsectionItem: Instance of 'ChecklistAnswerSectionItem', inputAnswer: W), ChecklistAnswerItem(id: 389, sectionItemId: 16, checklistsectionItem: Instance of 'ChecklistAnswerSectionItem', inputAnswer: 123)]
edit 17
create 17 // it suppost not to call create api!!!
create 16 // it suppost not to call create api!!!
Something like this you can do it's not 100% solution but you can work around it. I have made mock you can use release classes and variables
void checkForAction() {
// Check list but sending ID you want
editChecklistAnswer(finalMap.keys.first);
}
Future editChecklistAnswer(int checklistAnswerId) async {
List<ChecklistAnswerItem> answerTable = answerTableList;
if (answerTable.isEmpty) {
//TODO:Submit
}
bool isAnswerExist = answerTable
.where((element) => element.sectionItemId == checklistAnswerId)
.isNotEmpty;
if (isAnswerExist) {
//TODO: Call Edit API
} else {
//TODO: Call crease API
}
}
final Map<int, String> finalMap = {1: 'W', 2: 'N', 3: 'Yes'};
final answerTableList = [
ChecklistAnswerItem(1),
ChecklistAnswerItem(2),
ChecklistAnswerItem(3),
];
class ChecklistAnswerItem {
final int sectionItemId;
ChecklistAnswerItem(this.sectionItemId);
}
Related
I have the following code. Dictionary is just a wrapper for a List of type String.
public Dictionary getDictionary(int size, String text) {
return restTemplate.execute(url, HttpMethod.GET, null, response -> {
BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()));
List<String> words = new ArrayList<>();
String line;
while((line = br.readLine()) != null){
if (isMatch(line, size, text)){
words.add(line.toLowerCase());
}
}
br.close();
return new Dictionary(words);
});
}
private boolean isMatch(String word, int size, String text) {
if(word.length() != size) {
return false;
}
return wordUtil.isAnagram(word, text);
}
I'm having a hard time test this method at the moment. The HTTP call just returns a list of words in plain text with new line separators.
I want to write a test where I can stub the response.getBody().
I.e. I want response.getBody() to return a bunch of words, and I'll assert that the returned Dictionary only contains the words that are of size size and that are an anagram of the string text.
Is this possible?
Thanks
It is possible to stub a method taking a callback, and execute the callback when the stub is called.
The idea is to:
use when / thenAnswer to execute code when the stubbed method is called
use invocationOnMock passed to thenAnswer to get the callback instance
call the callback, providing necessary params
#Test
void testExecute() {
String responseBody = "line1\nline2";
InputStream responseBodyStream = new ByteArrayInputStream(responseBody.getBytes());
ClientHttpResponse httpResponse = new MockClientHttpResponse(responseBodyStream, 200);
when(restTemplate.execute(any(URI.class), eq(HttpMethod.GET), eq(null), any())).thenAnswer(
invocationOnMock -> {
ResponseExtractor<MyDictionary> responseExtractor = invocationOnMock.getArgument(3);
return responseExtractor.extractData(httpResponse);
}
);
MyDictionary ret = aController.getDictionary(1, "text");
// assert ret against your expecations
}
Having said that, this seems to be a bit complicated for the task at hand. IMHO you will be better off if you separate the logic of dealing with Http from your business logic. Extract a method taking your inputStream, and test that separately.
Disclaimer: I'm totally new to Haxe, but I have experience in many other languages.
I have tests similar to the following:
function doTest(type:SomethingMagic, tests:Array<Array<Int>>) {
for (t in tests) {
var res = DoSomeMagicalWork(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7]);
assertEquals(type, res.type);
}
}
The problem with this is that the unit test framework, when run on many different arrays, doesn't give me the correct line for which the test failed. In other words, if I'm running this method with a bunch of arrays, like:
doTest(SOME_MAGIC_TYPE,
[[0, 0, 0, 1625, 0, 35, 0, 0, 0, 0, 0],
...
]);
and one of these lines fails, it doesn't tell me which line failed. Now, I know that I could probably restructure these tests to be a bit more intuitive anyway, but this was written by someone else, and I don't have the ability to change each of these at the moment.
What I'd like to do is the following:
function doTest(type:SomethingMagic, tests:Array<Array<Int>>) {
var number = 0;
for (t in tests) {
var res = DoSomeMagicalWork(t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7]);
assertEquals(type, res.type, "Test #" + number + " for type " + type);
number++;
}
}
So, basically, I'd like to be able to pass in some extra messaging information to the assertEquals function, similar to what one can do in other unit testing frameworks. Then, upon failure, it would output the standard assertion message, possibly appended by the additional message I sent as a parameter to the function. Originally, I thought it was as simple as sub-classing haxe.TestCase, but that doesn't appear to be quite as simple as I thought, due to the way Haxe interprets types (apparently).
Has anyone had success with something similar to this that could give me a recommendation on how to accomplish it?
If you want to only get the position of the error you can use haxe.PosInfos as the last argument of your doTest() function and pass that arguemnt to assertEquals() like this:
import haxe.unit.TestCase;
class Main {
static function main() {
var r = new haxe.unit.TestRunner();
r.add(new Test());
r.run();
}
}
class Test extends TestCase {
public function new() {
super();
}
public function testExample() {
doTest(1, 1);
doTest(1, 2);
doTest(3, 3);
}
function doTest(a:Int, b:Int, ?pos:haxe.PosInfos) {
assertEquals(a, b, pos);
}
}
Online example here
It will give you the position that called doTest() in the error:
Test::testExample() ERR: Main.hx:18(Test.testExample) - expected '1' but was '2'
Another option if you want to add a custom message is to catch the assertEquals() error and rethrow the currentTest with a custom error like this:
import haxe.unit.TestCase;
class Main {
static function main() {
var r = new haxe.unit.TestRunner();
r.add(new Test());
r.run();
}
}
class Test extends TestCase {
public function new() {
super();
}
public function testExample() {
doTest(1, 1, "Error on test 1");
doTest(1, 2, "Error on test 2");
doTest(3, 3, "Error on test 3");
}
function doTest(a:Int, b:Int, errorMsg:String, ?pos:haxe.PosInfos) {
try {
assertEquals(a, b, pos);
} catch(e:Dynamic) {
currentTest.error = errorMsg;
throw currentTest;
}
}
}
Online example here
Which will give you the following error:
Test::testExample() ERR: Main.hx:18(Test.testExample) - Error on test 2
You are effectively blending multiple tests into a single one. And Haxe cannot tell where your array element is defined (line number, etc)
What I suggest is to change the signature of doTest to accept Array<Int> instead of Array<Array<Int>> and call doTest multiple times instead of just once. Together with the suggestion from Justo, passing the pos object to assetEquals, you will get the position correctly.
I'm working with a project, where I have EMF model 'A' which is referenced in many other models 'B','C'... etc. What I want is I want to give a rename feature for these resources. So when user renames 'A', its references have to be updated.
Please provide some idea on it, if there is any frame work for this or I have to get all the references and then programmatically iterate and update the references.
I solved the same problem in another way.
The fundamental problem is that a referenced resource file might be renamed, and this breaks the references.
Instead of a refactoring that automatically updates all references I created a Repair File References command, which the user can invoke on an edited model.
The command performs these steps:
Prompts the user to select a missing resource to repair
Prompts the user to select a replacement file
Updates all objects in the model that has a proxy URI that matches the missing resource. Replaces proxies with resolved objects in the new resource.
If you still want to make a refactoring instead, I think you anyway can use my code as a starting point.
/**
* Locates and fixes unresolved references in a model.
*/
public class ReferenceRepairer {
public static final String COMMAND_ID = Activator.PLUGIN_ID + ".commands.repairReferences";
/**
* 1) Prompts the user to select a missing resource to repair
* 2) Prompts the user to select a replacement file
* 3) Updates all objects in the model with a proxy URI that matches the missing resource. Replaces proxies
* with resolved objects in the new resource.
*/
public static void repairResourceReference(Shell shell, EditingDomain editingDomain) {
Resource res = promptMissingResource(shell, editingDomain);
if (res == null) return;
IFile newFile = promptReplacementFile(shell);
if (newFile == null) return;
repairReferences(editingDomain, res, URI.createPlatformResourceURI(newFile.getFullPath().toString(), true));
}
private static void repairReferences(final EditingDomain editingDomain, Resource missingRes, final URI newUri) {
URI missingUri = missingRes.getURI();
// Create new resource for the replacement file
Resource newRes = editingDomain.getResourceSet().getResource(newUri, true);
Map<EObject, Collection<Setting>> proxies = UnresolvedProxyCrossReferencer.find(editingDomain.getResourceSet());
CompoundCommand repairRefsCommand = new CompoundCommand("Repair references") {
/**
* Disallow undo. The model changes could be undone, but it seems impossible to
* recreate a non-existent resource in the resource set.
*/
#Override
public boolean canUndo() {
return false;
}
};
// Resolve all proxies from this resource and repair reference to those objects
for (Entry<EObject, Collection<Setting>> entry : proxies.entrySet()) {
EObject proxy = entry.getKey();
URI proxyUri = EcoreUtil.getURI(proxy);
if (!proxyUri.trimFragment().equals(missingUri)) continue;
EObject resolved = newRes.getEObject(proxyUri.fragment());
if (resolved.eIsProxy()) continue;
// Update all objects that have references to the resolved proxy
for (Setting sett : entry.getValue()) {
if (sett.getEStructuralFeature().isMany()) {
#SuppressWarnings("unchecked")
EList<Object> valueList = (EList<Object>) sett.get(true);
int proxyIx = valueList.indexOf(proxy);
repairRefsCommand.append(SetCommand.create(editingDomain,
sett.getEObject(), sett.getEStructuralFeature(), resolved, proxyIx));
} else {
repairRefsCommand.append(SetCommand.create(editingDomain,
sett.getEObject(), sett.getEStructuralFeature(), resolved));
}
}
}
if (!repairRefsCommand.isEmpty()) {
editingDomain.getCommandStack().execute(repairRefsCommand);
}
// Remove the
editingDomain.getResourceSet().getResources().remove(missingRes);
}
private static IFile promptReplacementFile(Shell shell) {
ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(shell,
new WorkbenchLabelProvider(), new WorkbenchContentProvider());
dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
dialog.setTitle("Select Replacement Resource");
dialog.setMessage("Select a file which will replace the missing file.");
dialog.setValidator(new ISelectionStatusValidator() {
#Override
public IStatus validate(Object[] selection) {
if (selection.length == 0 || !(selection[0] instanceof IFile)) {
return ValidationStatus.error("The selected object is not a file.");
}
return new Status(IStatus.OK, Activator.PLUGIN_ID, "");
}
});
if (dialog.open() != Window.OK) return null;
return (IFile) dialog.getFirstResult();
}
private static Resource promptMissingResource(Shell shell, EditingDomain editingDomain) {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell,
new LabelProvider() {
#Override
public String getText(Object elem) {
return ((Resource) elem).getURI().toString();
}
})
{
/** Make dialog OK button enabled when there are errors, instead of vise-versa. */
#Override
protected void updateButtonsEnableState(IStatus status) {
Button okButton = getOkButton();
if (okButton != null && !okButton.isDisposed()) {
okButton.setEnabled(!status.isOK());
}
}
/** Disable filter text field */
#Override
protected Text createFilterText(Composite parent) {
Text text = super.createFilterText(parent);
text.setSize(0, 0);
text.setLayoutData(GridDataFactory.swtDefaults().exclude(true).create());
text.setVisible(false);
return text;
}
};
dialog.setTitle("Select Missing Resource");
dialog.setMessage(
"Select a URI of a missing resource file that should be replaced by an URI to an existing file.");
dialog.setElements(getMissingResources(editingDomain.getResourceSet().getResources()).toArray());
if (dialog.open() != Window.OK) return null;
return (Resource) dialog.getFirstResult();
}
private static List<Resource> getMissingResources(List<Resource> resources) {
List<Resource> missingResources = new ArrayList<>();
for (Resource res : resources) {
try {
if (res.getURI().isPlatformPlugin()) continue;
URL url = FileLocator.toFileURL(new URL(res.getURI().toString()));
java.net.URI uri = new java.net.URI(url.getProtocol(), "", "/" + url.getPath(), null);
if (!Files.exists(Paths.get(uri))) {
missingResources.add(res);
}
} catch (InvalidPathException | IOException | URISyntaxException exc) {
// Ignore. There mighe be weird Sirius resource in the resources set which we can't recognice
}
}
return missingResources;
}
}
Using Firebase to count the total records is done this way:
var table = new Firebase('http://beta.firebase.com/user/tablename');
table.on('value', function(snapshot) {
var count = 0;
snapshot.forEach(function() {
count++;
});
//count is now safe to use.
});
Is there a way to avoid enumeration by having a cached counter in a different path?
I was thinking in some "counter" object which keeps the history of changes and the last computed value.
counter:
{
value: 672,
history:
{
+2, -4, +1, +1, +1
}
}
in a transaction then:
pick one history item, update the value, remove the history item.
Also who would be responsible of doing this?
Here's an example that combines the idea of a counter with an incremental, numeric ID. For your use case, you could skip the ID portion, but the principles are still the same.
The core of this is a transaction that, when you create a new record, adds one to your counter:
var fb = new Firebase('http://beta.firebase.com');
// stores incremental id before adding record
function incRecord(data) {
// increment the counter
fb.child('counter/value').transaction(function(currentValue) {
return (currentValue||0) + 1
}, function(err, committed, ss) {
if( err ) {
console.error(err);
}
else if( committed ) {
// if you want to pass the counter into the data,
// just use ss.val() here to fetch it
addRecord(data);
// could also store an audit history about changes to the counter, assuming we had a user ID or something to that effect with this:
// but you don't need this history to increment it
// fb.child('counter/history/'+ss.val()).set(userId);
}
});
}
// creates new record
function addRecord(data) {
// you could pass the record value here, I just set the value to "record #<id>"
fb.child('records').push(data, function(err) {
err && console.error(err);
});
}
Then invoke it by calling something like this:
incRecord({ hello: 'world' });
If I had a file whose contents looked like:
{"one": 1}
{"two": 2}
I could simply parse each separate line as a separate JSON object (using JsonCpp). But what if the structure of the file was less convenient like this:
{
"one":1
}
{
"two":2
}
No one has mentioned arrays:
[
{"one": 1},
{"two": 2}
]
Is valid JSON and might do what the OP wants.
Neither example in your question is a valid JSON object; a JSON object may only have one root. You have to split the file into two objects, then parse them.
You can use http://jsonlint.com to see if a given string is valid JSON or not.
So I recommend either changing what ever is dumping multiple JSON objects into a single file to do it in separate files, or to put each object as a value in one JSON root object.
If you don't have control over whatever is creating these, then you're stuck parsing the file yourself to pick out the different root objects.
Here's a valid way of encoding those data in a JSON object:
{
"one": 1,
"two": 2
}
If your really need separate objects, you can do it like this:
{
"one":
{
"number": 1
},
"two":
{
"number": 2
}
}
Rob Kennedy is right. Calling it a second time would extract the next object, and so on.Most of the json lib can not help you to do all in a single root. Unless you are using more high end framework in QT.
You can also use this custom function to parse multiple root elements even if you have complex objects.
static getParsedJson(jsonString) {
const parsedJsonArr = [];
let tempStr = '';
let isObjStartFound = false;
for (let i = 0; i < jsonString.length; i += 1) {
if (isObjStartFound) {
tempStr += jsonString[i];
if (jsonString[i] === '}') {
try {
const obj = JSON.parse(tempStr);
parsedJsonArr.push(obj);
tempStr = '';
isObjStartFound = false;
} catch (err) {
// console.log("not a valid JSON object");
}
}
}
if (!isObjStartFound && jsonString[i] === '{') {
tempStr += jsonString[i];
isObjStartFound = true;
}
}
return parsedJsonArr;
}