[Major] moved to Jersey 2.11
fixed muliple issues: - use language passed by client - test language - testing for enums
This commit is contained in:
parent
61e86deb15
commit
5140acdd42
24
pom.xml
24
pom.xml
|
@ -30,8 +30,8 @@
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<jersey.version>2.7</jersey.version>
|
<jersey.version>2.11</jersey.version>
|
||||||
<org.eclipse.persistence.version>2.5.1</org.eclipse.persistence.version>
|
<!-- <org.eclipse.persistence.version>2.5.1</org.eclipse.persistence.version> -->
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
@ -44,16 +44,16 @@
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- due to bug in persistence 2.5.0 -->
|
<!-- due to bug in persistence 2.5.0 -->
|
||||||
<dependency>
|
<!-- <dependency> -->
|
||||||
<groupId>org.eclipse.persistence</groupId>
|
<!-- <groupId>org.eclipse.persistence</groupId> -->
|
||||||
<artifactId>org.eclipse.persistence.moxy</artifactId>
|
<!-- <artifactId>org.eclipse.persistence.moxy</artifactId> -->
|
||||||
<version>${org.eclipse.persistence.version}</version>
|
<!-- <version>${org.eclipse.persistence.version}</version> -->
|
||||||
</dependency>
|
<!-- </dependency> -->
|
||||||
<dependency>
|
<!-- <dependency> -->
|
||||||
<groupId>org.eclipse.persistence</groupId>
|
<!-- <groupId>org.eclipse.persistence</groupId> -->
|
||||||
<artifactId>org.eclipse.persistence.antlr</artifactId>
|
<!-- <artifactId>org.eclipse.persistence.antlr</artifactId> -->
|
||||||
<version>${org.eclipse.persistence.version}</version>
|
<!-- <version>${org.eclipse.persistence.version}</version> -->
|
||||||
</dependency>
|
<!-- </dependency> -->
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package li.strolch.rest.endpoint;
|
package li.strolch.rest.endpoint;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
@ -24,6 +26,7 @@ import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.GenericEntity;
|
import javax.ws.rs.core.GenericEntity;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
@ -31,6 +34,7 @@ import javax.ws.rs.core.Response.Status;
|
||||||
import li.strolch.exception.StrolchException;
|
import li.strolch.exception.StrolchException;
|
||||||
import li.strolch.rest.RestfulStrolchComponent;
|
import li.strolch.rest.RestfulStrolchComponent;
|
||||||
import li.strolch.rest.StrolchSessionHandler;
|
import li.strolch.rest.StrolchSessionHandler;
|
||||||
|
import li.strolch.rest.helper.RestfulHelper;
|
||||||
import li.strolch.rest.model.Login;
|
import li.strolch.rest.model.Login;
|
||||||
import li.strolch.rest.model.LoginResult;
|
import li.strolch.rest.model.LoginResult;
|
||||||
import li.strolch.rest.model.LogoutResult;
|
import li.strolch.rest.model.LogoutResult;
|
||||||
|
@ -50,13 +54,10 @@ public class AuthenticationService {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
|
private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
|
||||||
|
|
||||||
@Context
|
|
||||||
HttpServletRequest request;
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response login(Login login) {
|
public Response login(Login login, @Context HttpServletRequest request, @Context HttpHeaders headers) {
|
||||||
|
|
||||||
LoginResult loginResult = new LoginResult();
|
LoginResult loginResult = new LoginResult();
|
||||||
GenericEntity<LoginResult> entity = new GenericEntity<LoginResult>(loginResult, LoginResult.class) {
|
GenericEntity<LoginResult> entity = new GenericEntity<LoginResult>(loginResult, LoginResult.class) {
|
||||||
|
@ -83,9 +84,12 @@ public class AuthenticationService {
|
||||||
Certificate certificate = sessionHandler.authenticate(origin, login.getUsername(), login.getPassword()
|
Certificate certificate = sessionHandler.authenticate(origin, login.getUsername(), login.getPassword()
|
||||||
.getBytes());
|
.getBytes());
|
||||||
|
|
||||||
|
Locale locale = RestfulHelper.getLocale(headers);
|
||||||
|
certificate.setLocale(locale);
|
||||||
|
|
||||||
loginResult.setSessionId(certificate.getAuthToken());
|
loginResult.setSessionId(certificate.getAuthToken());
|
||||||
loginResult.setUsername(certificate.getUsername());
|
loginResult.setUsername(certificate.getUsername());
|
||||||
loginResult.setLocale(certificate.getLocale().getLanguage() + "_" + certificate.getLocale().getCountry());
|
loginResult.setLocale(locale.toString());
|
||||||
loginResult.setParameters(certificate.getPropertyMap());
|
loginResult.setParameters(certificate.getPropertyMap());
|
||||||
|
|
||||||
return Response.ok().entity(entity).build();
|
return Response.ok().entity(entity).build();
|
||||||
|
@ -106,7 +110,7 @@ public class AuthenticationService {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("{authToken}")
|
@Path("{authToken}")
|
||||||
public Response logout(@PathParam("authToken") String authToken) {
|
public Response logout(@PathParam("authToken") String authToken, @Context HttpServletRequest request) {
|
||||||
|
|
||||||
LogoutResult logoutResult = new LogoutResult();
|
LogoutResult logoutResult = new LogoutResult();
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,14 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.GenericEntity;
|
import javax.ws.rs.core.GenericEntity;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import li.strolch.rest.RestfulStrolchComponent;
|
import li.strolch.rest.RestfulStrolchComponent;
|
||||||
|
import li.strolch.rest.helper.RestfulHelper;
|
||||||
import li.strolch.runtime.query.enums.EnumHandler;
|
import li.strolch.runtime.query.enums.EnumHandler;
|
||||||
import li.strolch.runtime.query.enums.StrolchEnum;
|
import li.strolch.runtime.query.enums.StrolchEnum;
|
||||||
|
|
||||||
|
@ -43,12 +46,14 @@ public class EnumQuery {
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("{name}")
|
@Path("{name}")
|
||||||
public Response getEnum(@PathParam("name") String name) {
|
public Response getEnum(@PathParam("name") String name, @Context HttpHeaders headers) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
EnumHandler enumHandler = RestfulStrolchComponent.getInstance().getContainer()
|
EnumHandler enumHandler = RestfulStrolchComponent.getInstance().getContainer()
|
||||||
.getComponent(EnumHandler.class);
|
.getComponent(EnumHandler.class);
|
||||||
StrolchEnum strolchEnum = enumHandler.getEnum(name, Locale.getDefault());
|
|
||||||
|
Locale locale = RestfulHelper.getLocale(headers);
|
||||||
|
StrolchEnum strolchEnum = enumHandler.getEnum(name, locale);
|
||||||
|
|
||||||
GenericEntity<StrolchEnum> entity = new GenericEntity<StrolchEnum>(strolchEnum, StrolchEnum.class) {
|
GenericEntity<StrolchEnum> entity = new GenericEntity<StrolchEnum>(strolchEnum, StrolchEnum.class) {
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Robert von Burg <eitch@eitchnet.ch>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package li.strolch.rest.helper;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import ch.eitchnet.utils.helper.StringHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
|
*/
|
||||||
|
public class RestfulHelper {
|
||||||
|
|
||||||
|
public static Locale getLocale(HttpHeaders headers) {
|
||||||
|
Locale locale;
|
||||||
|
if (headers == null || StringHelper.isEmpty(headers.getHeaderString(HttpHeaders.ACCEPT_LANGUAGE)))
|
||||||
|
locale = Locale.getDefault();
|
||||||
|
else
|
||||||
|
locale = headers.getAcceptableLanguages().get(0);
|
||||||
|
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,22 +16,22 @@
|
||||||
package li.strolch.rest.inspector.test;
|
package li.strolch.rest.inspector.test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.Application;
|
import javax.ws.rs.core.Application;
|
||||||
import javax.ws.rs.ext.ContextResolver;
|
|
||||||
|
|
||||||
|
import li.strolch.rest.endpoint.Inspector;
|
||||||
import li.strolch.testbase.runtime.RuntimeMock;
|
import li.strolch.testbase.runtime.RuntimeMock;
|
||||||
|
|
||||||
import org.glassfish.grizzly.http.server.HttpServer;
|
import org.glassfish.grizzly.http.server.HttpServer;
|
||||||
import org.glassfish.jersey.client.ClientConfig;
|
import org.glassfish.jersey.client.ClientConfig;
|
||||||
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
import org.glassfish.jersey.filter.LoggingFilter;
|
||||||
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
|
import org.glassfish.jersey.grizzly2.servlet.GrizzlyWebContainerFactory;
|
||||||
import org.glassfish.jersey.server.ResourceConfig;
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
import org.glassfish.jersey.server.ServerProperties;
|
||||||
|
import org.glassfish.jersey.server.TracingConfig;
|
||||||
import org.glassfish.jersey.test.JerseyTest;
|
import org.glassfish.jersey.test.JerseyTest;
|
||||||
import org.glassfish.jersey.test.TestProperties;
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -50,7 +50,7 @@ public abstract class AbstractRestfulTest extends JerseyTest {
|
||||||
private static HttpServer server;
|
private static HttpServer server;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() {
|
public static void beforeClass() throws IllegalArgumentException, IOException {
|
||||||
|
|
||||||
File rootPath = new File(RUNTIME_PATH);
|
File rootPath = new File(RUNTIME_PATH);
|
||||||
File configSrc = new File(CONFIG_SRC);
|
File configSrc = new File(CONFIG_SRC);
|
||||||
|
@ -58,7 +58,7 @@ public abstract class AbstractRestfulTest extends JerseyTest {
|
||||||
runtimeMock.mockRuntime(rootPath, configSrc);
|
runtimeMock.mockRuntime(rootPath, configSrc);
|
||||||
runtimeMock.startContainer();
|
runtimeMock.startContainer();
|
||||||
|
|
||||||
server = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, createApp());
|
server = GrizzlyWebContainerFactory.create(BASE_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
@ -69,26 +69,32 @@ public abstract class AbstractRestfulTest extends JerseyTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Application configure() {
|
protected Application configure() {
|
||||||
enable(TestProperties.LOG_TRAFFIC);
|
// enable(TestProperties.LOG_TRAFFIC);
|
||||||
enable(TestProperties.DUMP_ENTITY);
|
// enable(TestProperties.DUMP_ENTITY);
|
||||||
|
|
||||||
return createApp();
|
return createApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureClient(ClientConfig config) {
|
protected void configureClient(ClientConfig config) {
|
||||||
config.register(createMoxyJsonResolver());
|
//config.register(createMoxyJsonResolver());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceConfig createApp() {
|
public static ResourceConfig createApp() {
|
||||||
return new ResourceConfig().packages("li.strolch.rest.endpoint").register(createMoxyJsonResolver());
|
return new ResourceConfig()//
|
||||||
|
.packages(Inspector.class.getPackage().getName())//
|
||||||
|
//.register(createMoxyJsonResolver())
|
||||||
|
// Logging.
|
||||||
|
.register(LoggingFilter.class)
|
||||||
|
// Tracing support.
|
||||||
|
.property(ServerProperties.TRACING, TracingConfig.ON_DEMAND.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
|
// public static ContextResolver<MoxyJsonConfig> createMoxyJsonResolver() {
|
||||||
final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
|
// final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
|
||||||
Map<String, String> namespacePrefixMapper = new HashMap<String, String>(1);
|
// Map<String, String> namespacePrefixMapper = new HashMap<String, String>(1);
|
||||||
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
|
// namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
|
||||||
moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');
|
// moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');
|
||||||
return moxyJsonConfig.resolver();
|
// return moxyJsonConfig.resolver();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.Invocation.Builder;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
@ -68,6 +71,35 @@ public class AuthenticationTest extends AbstractRestfulTest {
|
||||||
assertNull(logoutResult.getMsg());
|
assertNull(logoutResult.getMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldUseRequestedLanguage() {
|
||||||
|
|
||||||
|
// login
|
||||||
|
Login login = new Login();
|
||||||
|
login.setUsername("jill");
|
||||||
|
login.setPassword("jill");
|
||||||
|
Entity<Login> loginEntity = Entity.entity(login, MediaType.APPLICATION_JSON);
|
||||||
|
Builder builder = target().path(ROOT_PATH).request(MediaType.APPLICATION_JSON);
|
||||||
|
builder = builder.acceptLanguage(Locale.ITALY);
|
||||||
|
Response result = builder.post(loginEntity);
|
||||||
|
assertEquals(Status.OK.getStatusCode(), result.getStatus());
|
||||||
|
LoginResult loginResult = result.readEntity(LoginResult.class);
|
||||||
|
assertNotNull(loginResult);
|
||||||
|
assertEquals("jill", loginResult.getUsername());
|
||||||
|
assertEquals(64, loginResult.getSessionId().length());
|
||||||
|
assertEquals(Locale.ITALY.toString(), loginResult.getLocale());
|
||||||
|
assertNull(loginResult.getMsg());
|
||||||
|
|
||||||
|
// logout
|
||||||
|
result = target().path(ROOT_PATH + "/" + loginResult.getSessionId()).request(MediaType.APPLICATION_JSON)
|
||||||
|
.delete();
|
||||||
|
assertEquals(Status.OK.getStatusCode(), result.getStatus());
|
||||||
|
assertNotNull(loginResult);
|
||||||
|
LogoutResult logoutResult = result.readEntity(LogoutResult.class);
|
||||||
|
assertNotNull(logoutResult);
|
||||||
|
assertNull(logoutResult.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotAuthenticate() {
|
public void shouldNotAuthenticate() {
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ package li.strolch.rest.inspector.test;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
@ -55,5 +57,20 @@ public class EnumTest extends AbstractRestfulTest {
|
||||||
assertNotNull(strolchEnum);
|
assertNotNull(strolchEnum);
|
||||||
assertEquals("salutation", strolchEnum.getName());
|
assertEquals("salutation", strolchEnum.getName());
|
||||||
assertEquals(3, strolchEnum.getValues().size());
|
assertEquals(3, strolchEnum.getValues().size());
|
||||||
|
assertEquals("Mrs", strolchEnum.getValue("mrs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldQueryGermanSalutation() {
|
||||||
|
|
||||||
|
// query
|
||||||
|
Response result = target().path(ROOT_PATH + "/salutation").request(MediaType.APPLICATION_JSON)
|
||||||
|
.acceptLanguage(Locale.GERMAN).get();
|
||||||
|
assertEquals(Status.OK.getStatusCode(), result.getStatus());
|
||||||
|
StrolchEnum strolchEnum = result.readEntity(StrolchEnum.class);
|
||||||
|
assertNotNull(strolchEnum);
|
||||||
|
assertEquals("salutation", strolchEnum.getName());
|
||||||
|
assertEquals(3, strolchEnum.getValues().size());
|
||||||
|
assertEquals("Frau", strolchEnum.getValue("mrs"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
<Parameter Id="ms" Name="Ms" Type="String" Value="Ms" />
|
<Parameter Id="ms" Name="Ms" Type="String" Value="Ms" />
|
||||||
<Parameter Id="mrs" Name="Mrs" Type="String" Value="Mrs" />
|
<Parameter Id="mrs" Name="Mrs" Type="String" Value="Mrs" />
|
||||||
</ParameterBag>
|
</ParameterBag>
|
||||||
|
<ParameterBag Id="de" Name="Salutations" Type="Enumeration">
|
||||||
|
<Parameter Id="mr" Name="Mr" Type="String" Value="Herr" />
|
||||||
|
<Parameter Id="ms" Name="Ms" Type="String" Value="Frau" />
|
||||||
|
<Parameter Id="mrs" Name="Mrs" Type="String" Value="Frau" />
|
||||||
|
</ParameterBag>
|
||||||
</Resource>
|
</Resource>
|
||||||
|
|
||||||
<Resource Id="sex" Name="Sex" Type="Enumeration">
|
<Resource Id="sex" Name="Sex" Type="Enumeration">
|
||||||
|
|
Loading…
Reference in New Issue