Skip to content

Commit

Permalink
handle openmrs objects that are in the form of enums, or parameters w…
Browse files Browse the repository at this point in the history
…ithin Set, List, etc
  • Loading branch information
mherman22 committed Nov 30, 2024
1 parent 55b8ce1 commit 6e07753
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public void addPathsWorksForCoreModels() throws NoSuchMethodException, Invocatio
IllegalAccessException, NoSuchFieldException {
SwaggerSpecificationCreator ssc = new SwaggerSpecificationCreator();

// reflect the swagger propperty and initSwagger method so we can setup for the main test
// reflect the swagger property and initSwagger method so we can set up for the main test
Field swagger = ssc.getClass().getDeclaredField("swagger");
swagger.setAccessible(true);
swagger.set(ssc, new Swagger());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,20 @@
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.properties.*;
import org.apache.commons.lang.StringUtils;
import org.openmrs.module.webservices.docs.swagger.core.property.EnumProperty;
import org.openmrs.module.webservices.rest.web.representation.Representation;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription;
import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static org.openmrs.module.webservices.rest.web.representation.Representation.DEFAULT;
Expand All @@ -38,6 +45,8 @@
*/
public class SwaggerGenerationUtil {

private static final Logger logger = LoggerFactory.getLogger(SwaggerGenerationUtil.class);

/**
* Generates the model for GET operations.
*
Expand Down Expand Up @@ -234,7 +243,8 @@ public static Property determinePropertyForField(Object resourceHandler, String
* @param field the field to generate the property for
* @return the Swagger property
*/
private static Property createPropertyForType(Class<?> type, String operationType, Field field) {
@SuppressWarnings("unchecked")
public static Property createPropertyForType(Class<?> type, String operationType, Field field) {
if (String.class.equals(type)) {
return new StringProperty();
} else if (Integer.class.equals(type) || int.class.equals(type)) {
Expand All @@ -248,20 +258,87 @@ private static Property createPropertyForType(Class<?> type, String operationTyp
} else if (Double.class.equals(type)) {
return new DoubleProperty();
} else if (isOpenMRSResource(type)) {
return new RefProperty("#/definitions/" + field.getName() + operationType);
if (type.isEnum()) {
return new EnumProperty((Class<? extends Enum<?>>) type);
} else {
return new RefProperty("#/definitions/" + StringUtils.capitalize(getModelName(String.valueOf(field.getType()))) + operationType);
}
} else if (Set.class.equals(type) || List.class.equals(type)) {
Class<?> elementType = getGenericTypeFromField(field);
if (elementType != null && isOpenMRSResource(elementType) ) {
return new ArrayProperty(new RefProperty("#/definitions/"
+ StringUtils.capitalize(getModelNameFromGenericType(elementType.getName())) + operationType));
}
return new ArrayProperty();
} else {
return new ObjectProperty();
}
}


/**
* Extracts the simple name from a fully qualified class name.
*
* @param qualifiedName the fully qualified class name to extract the simple name from (e.g., "org.openmrs.Patient")
* @return the simple class name (e.g., "Patient"), or the original string if no dot is present, or null if the input is null
*/
public static String getModelName(String qualifiedName) {
if (qualifiedName == null || !qualifiedName.contains(".")) {
return qualifiedName;
}

String simpleName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
simpleName = simpleName.replace("$", "");
return simpleName.substring(0, 1).toUpperCase() + simpleName.substring(1).toLowerCase();
}

public static String getModelNameFromGenericType(String name) {
if (name == null || !name.contains(".")) {
return name;
}

String simpleName = name.substring(name.lastIndexOf('.') + 1);
simpleName = simpleName.replace("$", "");
return simpleName.substring(0, 1).toUpperCase() + simpleName.substring(1);
}

/**
* Checks whether a class is an OpenMRS resource (e.g., references an OpenMRS data object).
*
* @param type the class to check
* @return true if the class represents an OpenMRS resource, false otherwise
*/
private static boolean isOpenMRSResource(Class<?> type) {
return type.getPackage().getName().startsWith("org.openmrs");
if (type == null) {
return false;
}

Package pkg = type.getPackage();
return pkg != null && pkg.getName().startsWith("org.openmrs");
}


/**
* Extracts the generic type argument of a field that represents a parameterized collection (e.g., List<T>, Set<T>).
* If the field is not parameterized or the generic type cannot be determined, it returns {@code null}.
*
* @param field the field whose generic type is to be determined
* @return the {@link Class} object representing the generic type parameter,
* or {@code null} if the field is not parameterized or the type cannot be resolved
*/
private static Class<?> getGenericTypeFromField(Field field) {
try {
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
Type[] typeArguments = parameterizedType.getActualTypeArguments();
if (typeArguments.length > 0 && typeArguments[0] instanceof Class) {
return (Class<?>) typeArguments[0];
}
}
} catch (Exception e) {
logger.warn("Could not determine the generic type for field: {}. This may not affect functionality.", field.getName(), e);
}
return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -834,13 +834,13 @@ private void addSubclassOperations() {

Map<String, Property> properties = definition.getProperties();

// 2. merge subclass properties into definition
// for (Map.Entry<String, Property> prop : resourceHandler.getGETModel(Representation.FULL).getProperties()
// .entrySet()) {
// if (properties.get(prop.getKey()) == null) {
// properties.put(prop.getKey(), prop.getValue());
// }
// }
// 2. merge subclass properties into definition
for (Map.Entry<String, Property> prop : SwaggerGenerationUtil.generateGETModel(resourceHandler, Representation.FULL).getProperties()
.entrySet()) {
if (properties.get(prop.getKey()) == null) {
properties.put(prop.getKey(), prop.getValue());
}
}

// 3. update description
post.setDescription("Certain properties may be required depending on type");
Expand Down Expand Up @@ -997,7 +997,6 @@ private void createDefinition(OperationEnum operationEnum, String resourceName,
DelegatingResourceHandler<?> resourceHandler) {

String definitionName = getSchemaName(resourceName, resourceParentName, operationEnum);
System.out.println("definition-name:" + definitionName);
Model model = null;
Model modelRef = null;
Model modelFull = null;
Expand Down
Loading

0 comments on commit 6e07753

Please sign in to comment.