Fix: Add missing files

cloud
Dmitry Peshehonov 3 years ago
parent 00aa52085c
commit 2889e2f66c

@ -0,0 +1,24 @@
package com.almworks.items.util;
import com.almworks.util.Pair;
import com.almworks.util.tests.BaseTestCase;
/**
* @author dyoma
*/
public class DBNamespaceTests extends BaseTestCase {
public void testReverseFullId() {
DBNamespace module = DBNamespace.moduleNs("module");
String moduleTypeId = module.type("TT.typeId", "").getId();
assertEquals("module:t:TT.typeId", moduleTypeId);
assertEquals(Pair.create("t", "TT.typeId"), module.reverseFullId(moduleTypeId));
DBNamespace local = module.subNs("local");
String localTypeId = local.type("TT.typeId", "").getId();
assertEquals("module:t:local.TT.typeId", localTypeId);
assertEquals(Pair.create("t", "TT.typeId"), local.reverseFullId(localTypeId));
assertEquals(Pair.create("t", "local.TT.typeId"), module.reverseFullId(localTypeId));
assertNull(local.reverseFullId(moduleTypeId));
}
}

@ -0,0 +1,128 @@
package com.almworks.jira.provider3.remotedata.issue.fields;
import com.almworks.items.entities.api.Entity;
import com.almworks.items.entities.api.EntityKey;
import com.almworks.items.entities.api.collector.transaction.EntityHolder;
import com.almworks.jira.provider3.remotedata.issue.EditIssueRequest;
import com.almworks.jira.provider3.services.upload.PostUploadContext;
import com.almworks.jira.provider3.sync.schema.ServerJira;
import com.almworks.restconnector.operations.RestServerInfo;
import com.almworks.util.LogHelper;
import com.almworks.util.Pair;
import org.almworks.util.Collections15;
import org.almworks.util.Const;
import org.jetbrains.annotations.NotNull;
import org.json.simple.JSONArray;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author dyoma
* @param <T> must implement {@link Object#equals(Object)}
*/
public abstract class DifferenceValue<T> extends BaseValue {
private static final String ADD = "add";
private static final String REMOVE = "remove";
private final List<T> myAdd;
private final List<T> myRemove;
private final List<T> myNewValue;
protected DifferenceValue(List<T> add, List<T> remove, List<T> newValue) {
super(true);
myAdd = add;
myRemove = remove;
myNewValue = newValue;
}
@NotNull
@Override
public String[] getFormValue(RestServerInfo serverInfo) {
ArrayList<String> result = Collections15.arrayList();
for (T value : myNewValue) {
String id = extractFormId(value);
if (!id.isEmpty()) result.add(id);
}
return result.toArray(Const.EMPTY_STRINGS);
}
protected abstract String extractFormId(@NotNull T value);
@Override
public void addChange(EditIssueRequest edit) {
String fieldId = getFieldId();
if (edit.getFieldInfo(fieldId) == null) return; // No such field
if (!needsUpload(edit.getServerInfo())) return;
JSONArray changes = new JSONArray();
addChanges(edit, changes, myAdd, ADD);
addChanges(edit, changes, myRemove, REMOVE);
if (changes.isEmpty()) return;
edit.addEdit(this, fieldId, changes);
}
protected abstract void addChanges(EditIssueRequest edit, JSONArray target, List<T> change, String operation);
protected abstract String getFieldId();
@Override
public boolean isChanged() {
return !myAdd.isEmpty() || !myRemove.isEmpty();
}
@Override
protected void doFinishUpload(long issueItem, EntityHolder issue, PostUploadContext context) {
EntityKey<Collection<Entity>> key = getIssueKey();
if (isUploadDone(loadCurrentState(issue, key, this::readValue), key, myAdd, myRemove) != null) return;
context.reportUploaded(issueItem, ServerJira.toLinkSetAttribute(key));
}
/**
* @param <T> MUST implement {@link Object#equals(Object)}
* @return null if current state has all adds and no removes (everything has been uploaded right)<br>
* [notAdded, notRemoved] pair if at least one add is missing or remove present. Both lists are not-null.
*/
public static <T> Pair<List<T>, List<T>> isUploadDone(List<T> current, EntityKey<Collection<Entity>> key, List<T> expectedAdd, List<T> expectedRemove) {
List<T> notAdded = new ArrayList<>();
for (T entity : expectedAdd)
if (!current.contains(entity)) {
LogHelper.warning("Failed to add", entity, key);
notAdded.add(entity);
}
List<T> notRemoved = new ArrayList<>();
for (T entity : expectedRemove) {
if (current.contains(entity)) {
LogHelper.warning("Failed to remove", entity, key);
notRemoved.add(entity);
}
}
if (!notAdded.isEmpty() || !notRemoved.isEmpty()) {
LogHelper.warning("Server state", current, key);
return Pair.create(notAdded, notRemoved);
}
return null;
}
@NotNull
public static <T> List<T> loadCurrentState(EntityHolder issue, EntityKey<Collection<Entity>> key, Function<EntityHolder, T> readValue) {
EntityHolder[] serverHolders = issue.getReferenceCollection(key);
return Arrays.stream(serverHolders)
.map(readValue).filter(Objects::nonNull)
.collect(Collectors.toList());
}
protected abstract T readValue(EntityHolder holder);
protected abstract EntityKey<Collection<Entity>> getIssueKey();
protected String toString(Object descriptor) {
return "Upload " + descriptor + "[+" + myAdd + ",-" + myRemove + "]";
}
@Override
public String checkInitialState(EntityHolder issue) {
return null; // No test because of uploading difference
}
}

@ -0,0 +1,151 @@
package com.almworks.jira.provider3.remotedata.issue.fields;
import com.almworks.items.api.DBItemType;
import com.almworks.items.entities.api.Entity;
import com.almworks.items.entities.api.collector.transaction.EntityHolder;
import com.almworks.items.sync.ItemVersion;
import com.almworks.jira.provider3.schema.User;
import com.almworks.jira.provider3.services.upload.UploadJsonUtil;
import com.almworks.jira.provider3.sync.download2.rest.EntityParser;
import com.almworks.jira.provider3.sync.download2.rest.JRUser;
import com.almworks.jira.provider3.sync.download2.rest.JsonEntityParser;
import com.almworks.jira.provider3.sync.download2.rest.LoadedEntity;
import com.almworks.jira.provider3.sync.schema.ServerUser;
import com.almworks.restconnector.json.JSONKey;
import com.almworks.util.LogHelper;
import com.almworks.util.collections.Convertor;
import org.almworks.util.Util;
import org.jetbrains.annotations.NotNull;
import org.json.simple.JSONObject;
/**
* Supports for different user identities by {@link ServerUser#ACCOUNT_ID}.
* If an user has accountId value, assumes that user's connection supports accountId and uses it as preferred identity.
* @author dyoma
*/
public class JsonUserParser implements JsonEntityParser {
public static final JsonUserParser INSTANCE = new JsonUserParser(ServerUser.PARSER);
private final EntityParser myParser;
private JsonUserParser(EntityParser parser) {
assert parser != null;
myParser = parser;
}
@Override
public EntityParser getParser() {
return myParser;
}
@Override
public DBItemType getType() {
return User.DB_TYPE;
}
@Override
public Convertor<Object, Entity> createConvertor() {
return new Convertor<Object, Entity>() {
@Override
public Entity convert(Object value) {
return ServerUser.create(value, myParser);
}
};
}
@Override
public JsonEntityParser withParser(EntityParser parser) {
return new JsonUserParser(parser);
}
@Override
public LoadedUser readValue(ItemVersion value) {
if (value == null || value.getItem() <= 0) return null;
return new LoadedUser(value.getValue(User.NAME), value.getValue(User.ACCOUNT_ID));
}
@Override
public LoadedUser readValue(EntityHolder value) {
if (value == null) return null;
return new LoadedUser(value.getScalarValue(ServerUser.NAME), value.getScalarValue(ServerUser.ACCOUNT_ID));
}
private static final Convertor<Object, Entity> CONVERTOR = new Convertor<Object, Entity>() {
@Override
public Entity convert(Object value) {
return ServerUser.create(value, ServerUser.PARSER);
}
};
public static JSONKey<Entity> jsonKey(String key) {
return new JSONKey<>(key, CONVERTOR);
}
public static class LoadedUser implements LoadedEntity {
private final String myDisplayName;
private final String myAccountId;
public LoadedUser(String displayName, String accountId) {
if (accountId != null) {
accountId = accountId.trim();
if (accountId.isEmpty()) accountId = null;
LogHelper.assertError(accountId == null || accountId.length() > 5, "Too short accountId:", accountId, displayName);
}
LogHelper.assertError(accountId != null, "LoadedUser misses both username and accountID", displayName);
myDisplayName = displayName;
myAccountId = accountId;
}
public String getAccountId() {
return myAccountId;
}
@NotNull
@Override
public String getDisplayableText() {
return myDisplayName != null ? myDisplayName : myAccountId;
}
@NotNull
@Override
public String getFormValueId() {
return myAccountId;
}
@Override
public JSONObject toJson() {
if (myAccountId != null) return UploadJsonUtil.object(JRUser.ACCOUNT_ID.getName(), myAccountId);
LogHelper.warning("LoadedUser misses any JSON identity:", this);
return null;
}
@Override
public int hashCode() {
if (myAccountId != null) return myAccountId.hashCode();
return super.hashCode(); // This instance is not equal to any other (except itself)
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
LoadedUser other = Util.castNullable(LoadedUser.class, obj);
if (other == null) return false;
if (myAccountId != null && other.myAccountId != null) return myAccountId.equals(other.myAccountId);
return false;
}
@Override
public String toString() {
return "LoadedUser{" +
"myDisplayName='" + myDisplayName + '\'' +
", myAccountId='" + myAccountId + '\'' +
'}';
}
public boolean sameUser(EntityHolder user) {
if (user == null) return false;
String userAccountId = user.getScalarValue(ServerUser.ACCOUNT_ID);
if (myAccountId != null && userAccountId != null) return myAccountId.equals(userAccountId);
return true;
}
}
}

@ -0,0 +1,99 @@
package com.almworks.jira.provider3.sync.download2.rest;
import com.almworks.jira.provider3.services.upload.UploadJsonUtil;
import com.almworks.util.LogHelper;
import org.almworks.util.Util;
import org.jetbrains.annotations.NotNull;
import org.json.simple.JSONObject;
import java.util.Objects;
/**
* Represents an entity with identity and displayable text.
* An instance may represent itself as JSON.<br>
* Implementors MUST implement {@link Object#equals(Object)}
* @author dyoma
*/
public interface LoadedEntity {
/**
* @return human-friendly display name of the entity
*/
@NotNull
String getDisplayableText();
@Override
int hashCode();
/**
* Checks for equality against other LoadedEntity.
* Entities are equal if they identify the same object.
* If entities has different values those don't affect identity, the entities MUST be equal.
*/
@Override
boolean equals(Object obj);
/**
* @return an identifier of the entity to use for upload via HTML forms (when API calls are not available)
*/
@NotNull
String getFormValueId();
/**
* @return default JSON presentation of this entity. The JSON is expected to contain only identity, so it could be used to update reference fields.
*/
JSONObject toJson();
class Simple<I> implements LoadedEntity {
private final String myJsonIdKey;
private final I myId;
private final String myDisplayableText;
public Simple(String jsonIdKey, I id, String displayableText) {
myJsonIdKey = jsonIdKey;
myId = id;
myDisplayableText = displayableText != null ? displayableText : myId.toString();
}
@NotNull
@Override
public String getFormValueId() {
return myId != null ? myId.toString() : "";
}
@Override
public JSONObject toJson() {
if (myId == null) {
LogHelper.error("Missing id", this);
return null;
}
return UploadJsonUtil.object(myJsonIdKey, String.valueOf(myId));
}
public I getId() {
return myId;
}
@NotNull
@Override
public String getDisplayableText() {
return myDisplayableText;
}
@Override
public int hashCode() {
return Objects.hash(myJsonIdKey, myId);
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
Simple<?> other = Util.castNullable(Simple.class, obj);
return other != null && myJsonIdKey.equals(other.myJsonIdKey) && Objects.equals(myId, other.myId);
}
@Override
public String toString() {
return String.format("(%s=%s %s)", myJsonIdKey, myId, myDisplayableText);
}
}
}

@ -0,0 +1,136 @@
TYPE: Entity[TYPE:'types.attach']
EntityKey[attach.author:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[attach.date:Date]=21 Dec 2019 11:15:05.970 GMT
EntityKey[attach.fileName:String]=attachment.txt
EntityKey[attach.fileUrl:String]=https://pdyoma.atlassian.net/secure/attachment/10000/attachment.txt
EntityKey[attach.id:Int]=10000
EntityKey[attach.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
EntityKey[attach.mime:String]=text/plain
EntityKey[attach.sizeStr:String]=10
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.attach']<no resolution>
TYPE: Entity[TYPE:'types.comment']
EntityKey[comment.author:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[comment.created:Date]=21 Dec 2019 11:15:06.646 GMT
EntityKey[comment.editor:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[comment.id:Int]=10105
EntityKey[comment.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
EntityKey[comment.security:Entity]=<null>
EntityKey[comment.text:String]=comment
EntityKey[comment.updated:Date]=21 Dec 2019 11:15:06.646 GMT
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.comment']<no resolution>
TYPE: Entity[TYPE:'types.issue']
EntityKey[cf.CONN-ECTI-ON_I-D.editableDay.customfield_10211:Int]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.editableDecimal.customfield_10129:Decimal]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.multiEnum.customfield_10215:Entity COLLECTION]={}
EntityKey[cf.CONN-ECTI-ON_I-D.multiEnumRO.customfield_10127:Entity COLLECTION]={Entity[type.CONN-ECTI-ON_I-D.customfield_10127](customField.enum.idString=1)}
EntityKey[cf.CONN-ECTI-ON_I-D.multiLabels.labels:Entity COLLECTION]={Entity[type.CONN-ECTI-ON_I-D.labels](customField.enum.idString=label1)}
EntityKey[cf.CONN-ECTI-ON_I-D.readonlyDateTime.customfield_10100:Date]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.singleEnum.customfield_10213:Entity]=Entity[type.CONN-ECTI-ON_I-D.customfield_10213](customField.enum.idString=10100)
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10000:String]={}
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10102:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10103:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10104:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10105:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10106:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10107:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10108:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10122:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10123:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10128:String]=0|hzzzzz:
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10212:String]=<null>
EntityKey[cf.CONN-ECTI-ON_I-D.unknownKind.customfield_10214:String]=<null>
EntityKey[issue.affectedVersions:Entity COLLECTION]={}
EntityKey[issue.assignee:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[issue.components:Entity COLLECTION]={}
EntityKey[issue.created:Date]=12 Nov 2016 12:34:12.742 GMT
EntityKey[issue.description:String]=descr
EntityKey[issue.due:Int]=<null>
EntityKey[issue.environment:String]=<null>
EntityKey[issue.fixVersions:Entity COLLECTION]={}
EntityKey[issue.id:Int]=10000
EntityKey[issue.key:String]=TP-1
EntityKey[issue.originalEstimate:Int]=<null>
EntityKey[issue.parent:Entity]=<null>
EntityKey[issue.priority:Entity]=Entity[types.priority](entity.id=4)
EntityKey[issue.project:Entity]=Entity[types.project](entity.id=10000, project.key=TP)
EntityKey[issue.remainEstimate:Int]=3600
EntityKey[issue.reporter:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[issue.resolution:Entity]=Entity[types.resolution](entity.id=10000)
EntityKey[issue.resolved:Date]=21 Dec 2019 11:23:40.193 GMT
EntityKey[issue.security:Entity]=<null>
EntityKey[issue.status:Entity]=Entity[types.status](entity.id=10001)
EntityKey[issue.summary:String]=Ensure setup is complete 1122win
EntityKey[issue.timeSpent:Int]=1920300
EntityKey[issue.type:Entity]=Entity[types.issueType](entity.id=10000)
EntityKey[issue.updated:Date]=25 Feb 2020 13:34:51.706 GMT
EntityKey[issue.watchCount:Int]=1
EntityKey[issue.watching:bool]=true
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.issue']<no resolution>
EntityKey[sys.store.downloadStage.mark:com.almworks.items.entities.dbwrite.downloadstage.DownloadStageMark HINT]=DownloadStage[FULL]
TYPE: Entity[TYPE:'types.link']
TYPE: Entity[TYPE:'types.linkType']
TYPE: Entity[TYPE:'types.worklog']
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.worklog']<no resolution>
EntityKey[worklog.author:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.comment:String]=<null>
EntityKey[worklog.created:Date]=31 Oct 2017 13:08:29.334 GMT
EntityKey[worklog.editor:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.id:Int]=10001
EntityKey[worklog.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
EntityKey[worklog.security:Entity]=<null>
EntityKey[worklog.started:Date]=05 Sep 2017 10:34:00.0 GMT
EntityKey[worklog.timeSeconds:Int]=1900860
EntityKey[worklog.updated:Date]=31 Oct 2017 13:08:29.334 GMT
------------------------------
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.worklog']<no resolution>
EntityKey[worklog.author:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.comment:String]=<null>
EntityKey[worklog.created:Date]=31 Oct 2017 13:08:30.418 GMT
EntityKey[worklog.editor:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.id:Int]=10002
EntityKey[worklog.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
EntityKey[worklog.security:Entity]=<null>
EntityKey[worklog.started:Date]=27 Sep 2017 05:25:00.0 GMT
EntityKey[worklog.timeSeconds:Int]=18240
EntityKey[worklog.updated:Date]=31 Oct 2017 13:08:30.418 GMT
------------------------------
EntityKey[sys.api.key.type:Entity HINT]=Entity[TYPE:'types.worklog']<no resolution>
EntityKey[worklog.author:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.comment:String]=prev
EntityKey[worklog.created:Date]=31 Oct 2017 13:08:31.276 GMT
EntityKey[worklog.editor:Entity]=Entity[types.user](user.accountId=557058:f7042e9f-c74c-416a-9b75-f08f781eda42)
EntityKey[worklog.id:Int]=10003
EntityKey[worklog.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
EntityKey[worklog.security:Entity]=<null>
EntityKey[worklog.started:Date]=27 Sep 2017 10:10:17.0 GMT
EntityKey[worklog.timeSeconds:Int]=1200
EntityKey[worklog.updated:Date]=31 Oct 2017 13:08:31.276 GMT
*** BAGS ***
Entity[TYPE:'types.attach']<no resolution>
EntityKey[attach.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
DELETE
Entity[types.attach](attach.fileUrl=https://pdyoma.atlassian.net/secure/attachment/10000/attachment.txt, attach.issue=Entity[types.issue])
------------------------------
Entity[TYPE:'types.comment']<no resolution>
EntityKey[comment.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
DELETE
Entity[types.comment](comment.id=10105, comment.issue=Entity[types.issue])
------------------------------
Entity[TYPE:'types.issue']<no resolution>
EntityKey[issue.parent:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
CHANGE: EntityKey[issue.parent:Entity]=<null>
------------------------------
Entity[TYPE:'types.link']<no resolution>
EntityKey[link.source:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
DELETE
------------------------------
Entity[TYPE:'types.link']<no resolution>
EntityKey[link.target:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
DELETE
------------------------------
Entity[TYPE:'types.worklog']<no resolution>
EntityKey[worklog.issue:Entity]=Entity[types.issue](issue.id=10000, issue.key=TP-1)
DELETE
Entity[types.worklog](worklog.id=10001, worklog.issue=Entity[types.issue])
Entity[types.worklog](worklog.id=10002, worklog.issue=Entity[types.issue])
Entity[types.worklog](worklog.id=10003, worklog.issue=Entity[types.issue])

@ -0,0 +1,65 @@
package com.almworks.jira.provider3.sync.download2.details;
import com.almworks.restconnector.json.JSONKey;
import com.almworks.restconnector.json.sax.JSONCollector;
import com.almworks.restconnector.json.sax.LocationHandler;
import com.almworks.restconnector.json.sax.PeekObjectEntry;
import com.almworks.util.LogHelper;
import com.almworks.util.commons.Procedure;
import org.almworks.util.Collections15;
import org.jetbrains.annotations.Nullable;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import java.io.IOException;
import java.util.HashSet;
public class CollectOperations {
private static final JSONKey<String> ID = JSONKey.text("id");
private final LocationHandler myIssueHandler = PeekObjectEntry.objectEntry("operations", PeekObjectEntry.objectEntry("linkGroups", new LocationHandler() {
@Override
public void visit(Location what, boolean start, @Nullable String key, @Nullable Object value) throws ParseException, IOException {
doVisit(what, start, key, value);
}
}));
private final LocationHandler myLinkHandler = JSONCollector.objectConsumer(new Procedure<JSONObject>() {
@Override
public void invoke(JSONObject link) {
String id = ID.getValue(link);
if (id == null) return; // Some links does not have ID (example: reference to XML: /si/jira.issueviews:issue-xml/<issueKey>/<issueKey>.xml
if (!myIds.add(id)) LogHelper.error("Duplicated id", id);
}
});
private int myInLinks = 0;
private final HashSet<String> myIds = Collections15.hashSet();
public LocationHandler getIssueHandler() {
return myIssueHandler;
}
public void doVisit(LocationHandler.Location what, boolean start, @Nullable String key, @Nullable Object value) throws ParseException, IOException {
if (what == LocationHandler.Location.TOP && start) {
myInLinks = 0;
return;
}
if (start && what == LocationHandler.Location.ENTRY && "links".equals(key)) {
myInLinks = 1;
return;
} else if (myInLinks == 0) return;
else if (start && what != LocationHandler.Location.PRIMITIVE) myInLinks++;
if (myInLinks >= 3) {
if (myInLinks == 3 && start) {
myLinkHandler.visit(LocationHandler.Location.TOP, true, null, null);
// System.out.println("************ START");
}
myLinkHandler.visit(what, start, key, value);
// System.out.println("Processing: " + what + " " + start + " " + key + " " + value);
if (myInLinks == 3 && !start) {
myLinkHandler.visit(LocationHandler.Location.TOP, false, null, null);
// System.out.println("************ END");
}
}
if (!start) myInLinks--;
}
}

@ -0,0 +1,53 @@
package com.almworks.jira.provider3.sync.download2.details;
import com.almworks.items.api.DBIdentifiedObject;
import com.almworks.items.entities.api.collector.transaction.EntityTransaction;
import com.almworks.items.entities.api.collector.typetable.TransactionTestUtil;
import com.almworks.items.sync.util.identity.DBIdentity;
import com.almworks.jira.provider3.custom.FieldKind;
import com.almworks.jira.provider3.custom.impl.CustomFieldsComponent;
import com.almworks.jira.provider3.custom.loadxml.FieldKeysLoader;
import com.almworks.jira.provider3.schema.Jira;
import com.almworks.jira.provider3.sync.ServerInfo;
import com.almworks.jira.provider3.sync.download2.TestResources;
import com.almworks.jira.provider3.sync.schema.*;
import com.almworks.util.tests.BaseTestCase;
import org.almworks.util.TypedKey;
import org.json.simple.parser.ParseException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class LoadDetailsRTests extends BaseTestCase {
private static final TestResources RESOURCES = TestResources.create(LoadDetailsRTests.class, "com/almworks/jira/provider3/sync/download2/details/");
private static Map<String,FieldKind> CUSTOM_FIELDS_MAP;
static {
try {
FieldKeysLoader loader = FieldKeysLoader.load("/com/almworks/jira/provider3/customFields.xml", CustomFieldsComponent.SCHEMA);
List<Map<TypedKey<?>, ?>> kinds = loader.getLoadedKinds();
CUSTOM_FIELDS_MAP = CustomFieldsComponent.createKindsMap(false, kinds);
} catch (Exception e) {
e.printStackTrace();
}
}
// https://pdyoma.atlassian.net/rest/api/2/issue/TP-1?expand=operations%2Ctransitions%2Cschema
public void testNewCloudUsers() throws IOException, ParseException {
runTest("newCloudUsers.json", "newCloudUsers.json.txt");
}
private static final String CONNECTION_ID = "CONN-ECTI-ON_I-D";
private void runTest(String source, String result) throws IOException, ParseException {
Object json = RESOURCES.loadJson(source);
CollectOperations operations = new CollectOperations();
RESOURCES.parseJsonResource(source, operations.getIssueHandler());
DBIdentifiedObject connectionObject = Jira.createConnectionObject(CONNECTION_ID);
DBIdentity connection = DBIdentity.fromDBObject(connectionObject);
EntityTransaction transaction = ServerInfo.priCreateTransaction(CONNECTION_ID, connection);
LoadDetails details = new LoadDetails(transaction, new CustomFieldsSchema.RestLoader(CUSTOM_FIELDS_MAP, CONNECTION_ID));
RESOURCES.parseJsonResource(source, details.createHandler("Test Issue"));
RESOURCES.assertTextEquals(result, TransactionTestUtil.printTransaction(transaction, ServerIssue.TYPE, ServerComment.TYPE, ServerLink.TYPE, ServerLinkType.TYPE, ServerWorklog.TYPE, ServerAttachment.TYPE));
}
}

@ -0,0 +1,17 @@
package com.almworks.jira.provider3.sync.schema;
import com.almworks.items.api.DBItemType;
import com.almworks.items.entities.api.Entity;
import com.almworks.util.tests.BaseTestCase;
/**
* @author dyoma
*/
public class ServerJiraTests extends BaseTestCase {
public void testReverseEntityType() {
Entity type = Entity.buildType("my.type");
DBItemType dbType = ServerJira.toItemType(type);
Entity reversedType = ServerJira.dbTypeToEntity(dbType);
assertEquals(type.getTypeId(), reversedType.getTypeId());
}
}
Loading…
Cancel
Save