Use separate index columns
This commit is contained in:
parent
96d6fa1b8a
commit
9c8d9db7e6
|
@ -1225,15 +1225,22 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
for (IBaseReference nextRef : allRefs) {
|
||||
IIdType nextId = nextRef.getReferenceElement();
|
||||
String nextIdText = nextId.getValue();
|
||||
if (nextIdText == null) {
|
||||
continue;
|
||||
}
|
||||
int qmIndex = nextIdText.indexOf('?');
|
||||
if (qmIndex != -1) {
|
||||
for (int i = qmIndex - 1; i >= 0; i--) {
|
||||
if (nextIdText.charAt(i) == '/') {
|
||||
if (i < nextIdText.length() - 1 && nextIdText.charAt(i + 1) == '?') {
|
||||
// Just in case the URL is in the form Patient/?foo=bar
|
||||
continue;
|
||||
}
|
||||
nextIdText = nextIdText.substring(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
String resourceTypeString = nextIdText.substring(0, nextIdText.indexOf('?'));
|
||||
String resourceTypeString = nextIdText.substring(0, nextIdText.indexOf('?')).replace("/", "");
|
||||
RuntimeResourceDefinition matchResourceDef = getContext().getResourceDefinition(resourceTypeString);
|
||||
if (matchResourceDef == null) {
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "invalidMatchUrlInvalidResourceType", nextId.getValue(), resourceTypeString);
|
||||
|
|
|
@ -1,25 +1,5 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -40,11 +40,6 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Field
|
||||
@Column(name = "SP_NAME", length = MAX_SP_NAME, nullable = false)
|
||||
private String myParamName;
|
||||
|
@ -61,9 +56,7 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
|||
@Column(name = "RES_TYPE", nullable = false)
|
||||
private String myResourceType;
|
||||
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
protected abstract Long getId();
|
||||
|
||||
public String getParamName() {
|
||||
return myParamName;
|
||||
|
|
|
@ -55,8 +55,8 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@SequenceGenerator(name = "RES_HISTORY_PID", sequenceName = "RES_HISTORY_PID")
|
||||
@SequenceGenerator(name = "SEQ_RESOURCE_HISTORY_ID", sequenceName = "SEQ_RESOURCE_HISTORY_ID")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_HISTORY_ID")
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
|
|
|
@ -23,7 +23,11 @@ package ca.uhn.fhir.jpa.entity;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
@ -45,6 +49,12 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_COORDS", sequenceName = "SEQ_SPIDX_COORDS")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_COORDS")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_LATITUDE")
|
||||
@Field
|
||||
public double myLatitude;
|
||||
|
@ -82,6 +92,11 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return myLatitude;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ import java.util.Date;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
@ -47,6 +51,12 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_DATE", sequenceName = "SEQ_SPIDX_DATE")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_DATE")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_VALUE_HIGH", nullable = true)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Field
|
||||
|
@ -92,6 +102,11 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public Date getValueHigh() {
|
||||
return myValueHigh;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ import java.math.BigDecimal;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
@ -49,6 +53,12 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_NUMBER", sequenceName = "SEQ_SPIDX_NUMBER")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_NUMBER")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_VALUE", nullable = true)
|
||||
@Field
|
||||
@NumericField
|
||||
|
@ -82,6 +92,11 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public BigDecimal getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ import java.math.BigDecimal;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
@ -51,6 +55,12 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_QUANTITY", sequenceName = "SEQ_SPIDX_QUANTITY")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
||||
@Field
|
||||
public String mySystem;
|
||||
|
@ -97,6 +107,11 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public String getSystem() {
|
||||
return mySystem;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,12 @@ package ca.uhn.fhir.jpa.entity;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -101,6 +105,17 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name="SEQ_SPIDX_STRING", sequenceName="SEQ_SPIDX_STRING")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "RES_ID", referencedColumnName="RES_ID", insertable=false, updatable=false)
|
||||
@ContainedIn
|
||||
private ResourceTable myResourceTable;
|
||||
|
||||
@Column(name = "SP_VALUE_EXACT", length = MAX_LENGTH, nullable = true)
|
||||
@Fields({
|
||||
@Field(name = "myValueText", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
|
||||
|
@ -113,15 +128,10 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
|
||||
private String myValueNormalized;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "RES_ID", referencedColumnName="RES_ID", insertable=false, updatable=false)
|
||||
@ContainedIn
|
||||
private ResourceTable myResourceTable;
|
||||
|
||||
|
||||
public ResourceIndexedSearchParamString() {
|
||||
}
|
||||
|
||||
|
||||
public ResourceIndexedSearchParamString(String theName, String theValueNormalized, String theValueExact) {
|
||||
setParamName(theName);
|
||||
setValueNormalized(theValueNormalized);
|
||||
|
@ -147,6 +157,11 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public String getValueExact() {
|
||||
return myValueExact;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ package ca.uhn.fhir.jpa.entity;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -43,6 +47,12 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Field()
|
||||
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
||||
public String mySystem;
|
||||
|
@ -80,6 +90,11 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public String getSystem() {
|
||||
return mySystem;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@ package ca.uhn.fhir.jpa.entity;
|
|||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -49,6 +53,12 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name="SEQ_SPIDX_URI", sequenceName="SEQ_SPIDX_URI")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SPIDX_URI")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_URI", nullable = true, length = MAX_LENGTH)
|
||||
@Field()
|
||||
public String myUri;
|
||||
|
@ -80,6 +90,11 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return myUri;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import javax.persistence.GenerationType;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
|
@ -144,14 +145,12 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
//@formatter:on
|
||||
private String myContentText;
|
||||
|
||||
@Column(name = "HAS_CONTAINED", nullable = true)
|
||||
private boolean myHasContainedResource;
|
||||
|
||||
@Column(name = "SP_HAS_LINKS")
|
||||
private boolean myHasLinks;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@SequenceGenerator(name="SEQ_RESOURCE_ID", sequenceName="SEQ_RESOURCE_ID")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_RESOURCE_ID")
|
||||
@Column(name = "RES_ID")
|
||||
private Long myId;
|
||||
|
||||
|
@ -346,10 +345,6 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isHasContainedResource() {
|
||||
return myHasContainedResource;
|
||||
}
|
||||
|
||||
public boolean isHasLinks() {
|
||||
return myHasLinks;
|
||||
}
|
||||
|
@ -390,10 +385,6 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
myContentText = theContentText;
|
||||
}
|
||||
|
||||
public void setHasContainedResource(boolean theHasContainedResource) {
|
||||
myHasContainedResource = theHasContainedResource;
|
||||
}
|
||||
|
||||
public void setHasLinks(boolean theHasLinks) {
|
||||
myHasLinks = theHasLinks;
|
||||
}
|
||||
|
|
|
@ -345,6 +345,40 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateInlineMatchUrlWithOneMatch2() {
|
||||
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch2";
|
||||
Bundle request = new Bundle();
|
||||
|
||||
myDaoConfig.setAllowInlineMatchUrlReferences(true);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().addGiven("Heute");
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.setId("Patient/" + methodName);
|
||||
IIdType id = myPatientDao.update(p, mySrd).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
Observation o = new Observation();
|
||||
o.getCode().setText("Some Observation");
|
||||
o.getSubject().setReference("Patient/?given=heute");
|
||||
request.addEntry().setResource(o).getRequest().setMethod(HTTPVerb.POST);
|
||||
|
||||
Bundle resp = mySystemDao.transaction(myRequestDetails, request);
|
||||
assertEquals(1, resp.getEntry().size());
|
||||
|
||||
BundleEntryComponent respEntry = resp.getEntry().get(0);
|
||||
assertEquals(Constants.STATUS_HTTP_201_CREATED + " Created", respEntry.getResponse().getStatus());
|
||||
assertThat(respEntry.getResponse().getLocation(), containsString("Observation/"));
|
||||
assertThat(respEntry.getResponse().getLocation(), endsWith("/_history/1"));
|
||||
assertEquals("1", respEntry.getResponse().getEtag());
|
||||
|
||||
o = myObservationDao.read(new IdType(respEntry.getResponse().getLocationElement()), mySrd);
|
||||
assertEquals(id.toVersionless().getValue(), o.getSubject().getReference());
|
||||
assertEquals("1", o.getIdElement().getVersionIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateInlineMatchUrlWithNoMatches() {
|
||||
String methodName = "testTransactionCreateInlineMatchUrlWithNoMatches";
|
||||
|
|
|
@ -116,6 +116,32 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateConditional() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
patient.addName().addFamily("Tester").addGiven("Raghad");
|
||||
|
||||
MethodOutcome output1 = ourClient
|
||||
.update()
|
||||
.resource(patient)
|
||||
.conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100")
|
||||
.execute();
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
patient.addName().addFamily("Tester").addGiven("Raghad");
|
||||
|
||||
MethodOutcome output2 = ourClient
|
||||
.update()
|
||||
.resource(patient)
|
||||
.conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100")
|
||||
.execute();
|
||||
|
||||
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
|
||||
}
|
||||
|
||||
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(get);
|
||||
|
|
|
@ -136,7 +136,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test {
|
|||
Set<TermConcept> concepts;
|
||||
Set<String> codes;
|
||||
|
||||
concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "ChildAA");
|
||||
concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA");
|
||||
codes = toCodes(concepts);
|
||||
assertThat(codes, containsInAnyOrder("ParentA", "childAA"));
|
||||
|
||||
|
|
|
@ -241,6 +241,12 @@
|
|||
first query. This should improve performance when searching against large
|
||||
datasets.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server database design has been adjusted
|
||||
so that different tables use different sequences
|
||||
to generate their indexes, resulting in more sequential
|
||||
resource IDs being assigned by the server
|
||||
<action>
|
||||
</release>
|
||||
<release version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue