-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add validation to Registrar. Add RegistrarTest to exercise it. #361
Changes from 15 commits
4b3824a
e0c6de0
a6b72b2
bf78213
5e9de32
00ac587
b60ecdb
2ad6e87
d159768
6793eff
0f662bb
167e8de
eeb32b4
cc2f8b6
7afa5b0
bac9875
fdfda2e
45ea044
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
import com.fasterxml.jackson.databind.util.ISO8601DateFormat; | ||
import com.github.fge.jsonschema.core.load.configuration.LoadingConfiguration; | ||
import com.github.fge.jsonschema.core.load.download.URIDownloader; | ||
import com.github.fge.jsonschema.core.report.ProcessingReport; | ||
import com.github.fge.jsonschema.main.JsonSchema; | ||
import com.github.fge.jsonschema.main.JsonSchemaFactory; | ||
import com.google.api.services.cloudiot.v1.model.Device; | ||
|
@@ -29,6 +30,7 @@ | |
import java.io.FilenameFilter; | ||
import java.io.InputStream; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.math.BigInteger; | ||
import java.net.URI; | ||
import java.time.Duration; | ||
|
@@ -98,6 +100,9 @@ public class Registrar { | |
private Duration idleLimit; | ||
private Set<String> cloudDevices; | ||
private Metadata siteMetadata; | ||
private Map<String, Map<String, String>> lastErrorSummary; | ||
private boolean validateMetadata = false; | ||
private List<String> deviceList; | ||
|
||
/** | ||
* Main entry point for registrar. | ||
|
@@ -107,33 +112,11 @@ public class Registrar { | |
public static void main(String[] args) { | ||
ArrayList<String> argList = new ArrayList<>(List.of(args)); | ||
Registrar registrar = new Registrar(); | ||
try { | ||
boolean processAllDevices = processArgs(argList, registrar); | ||
|
||
if (registrar.schemaBase == null) { | ||
registrar.setToolRoot(null); | ||
} | ||
|
||
registrar.loadSiteMetadata(); | ||
|
||
if (processAllDevices) { | ||
registrar.processDevices(); | ||
} else { | ||
registrar.processDevices(argList); | ||
} | ||
|
||
registrar.writeErrors(); | ||
registrar.shutdown(); | ||
} catch (ExceptionMap em) { | ||
ExceptionMap.format(em, ERROR_FORMAT_INDENT).write(System.err); | ||
throw new RuntimeException("mapped exceptions", em); | ||
} catch (Exception ex) { | ||
ex.printStackTrace(); | ||
throw new RuntimeException("main exception", ex); | ||
} | ||
processArgs(argList, registrar); | ||
registrar.execute(); | ||
} | ||
|
||
private static boolean processArgs(List<String> argList, Registrar registrar) { | ||
public static void processArgs(List<String> argList, Registrar registrar) { | ||
while (argList.size() > 0) { | ||
String option = argList.remove(0); | ||
switch (option) { | ||
|
@@ -155,17 +138,39 @@ private static boolean processArgs(List<String> argList, Registrar registrar) { | |
case "-l": | ||
registrar.setIdleLimit(argList.remove(0)); | ||
break; | ||
case "-t": | ||
registrar.setValidateMetadata(true); | ||
break; | ||
case "--": | ||
return false; | ||
break; | ||
default: | ||
if (option.startsWith("-")) { | ||
throw new RuntimeException("Unknown cmdline option " + option); | ||
} | ||
// Add the current non-option back into the list and use it as device names list. | ||
argList.add(0, option); | ||
return false; | ||
registrar.setDeviceList(argList); | ||
return; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
public void execute() { | ||
try { | ||
if (schemaBase == null) { | ||
setToolRoot(null); | ||
} | ||
loadSiteMetadata(); | ||
processDevices(); | ||
writeErrors(); | ||
shutdown(); | ||
} catch (ExceptionMap em) { | ||
ExceptionMap.format(em, ERROR_FORMAT_INDENT).write(System.err); | ||
throw new RuntimeException("mapped exceptions", em); | ||
} catch (Exception ex) { | ||
ex.printStackTrace(); | ||
throw new RuntimeException("main exception", ex); | ||
} | ||
} | ||
|
||
private void setIdleLimit(String option) { | ||
|
@@ -177,6 +182,14 @@ private void setUpdateFlag(boolean update) { | |
updateCloudIoT = update; | ||
} | ||
|
||
private void setValidateMetadata(boolean validateMetadata) { | ||
this.validateMetadata = validateMetadata; | ||
} | ||
|
||
private void setDeviceList(List<String> deviceList) { | ||
this.deviceList = deviceList; | ||
} | ||
|
||
private void setFeedTopic(String feedTopic) { | ||
System.err.println("Sending device feed to topic " + feedTopic); | ||
feedPusher = new PubSubPusher(projectId, feedTopic); | ||
|
@@ -186,6 +199,10 @@ private void setFeedTopic(String feedTopic) { | |
} | ||
} | ||
|
||
protected Map<String, Map<String, String>> getLastErrorSummary() { | ||
return lastErrorSummary; | ||
} | ||
|
||
private void writeErrors() throws Exception { | ||
Map<String, Map<String, String>> errorSummary = new TreeMap<>(); | ||
DeviceExceptionManager dem = new DeviceExceptionManager(siteDir); | ||
|
@@ -223,9 +240,10 @@ private void writeErrors() throws Exception { | |
String version = Optional.ofNullable(System.getenv(UDMI_VERSION_KEY)).orElse("unknown"); | ||
errorSummary.put(VERSION_KEY, Map.of(VERSION_MAIN_KEY, version)); | ||
OBJECT_MAPPER.writeValue(summaryFile, errorSummary); | ||
lastErrorSummary = errorSummary; | ||
} | ||
|
||
private void setSitePath(String sitePath) { | ||
protected void setSitePath(String sitePath) { | ||
Preconditions.checkNotNull(SCHEMA_NAME, "schemaName not set yet"); | ||
siteDir = new File(sitePath); | ||
summaryFile = new File(siteDir, REGISTRATION_SUMMARY_JSON); | ||
|
@@ -259,7 +277,7 @@ private String getGenerationString() { | |
} | ||
|
||
private void processDevices() { | ||
processDevices(null); | ||
processDevices(this.deviceList); | ||
} | ||
|
||
private void processDevices(List<String> devices) { | ||
|
@@ -638,7 +656,7 @@ private Map<String, LocalDevice> loadDevices(File siteDir, File devicesDir, | |
localDevices.computeIfAbsent( | ||
deviceName, | ||
keyName -> new LocalDevice(siteDir, devicesDir, deviceName, schemas, generation, | ||
siteMetadata)); | ||
siteMetadata, validateMetadata)); | ||
try { | ||
localDevice.loadCredentials(); | ||
} catch (Exception e) { | ||
|
@@ -659,15 +677,15 @@ private Map<String, LocalDevice> loadDevices(File siteDir, File devicesDir, | |
return localDevices; | ||
} | ||
|
||
private void setProjectId(String projectId) { | ||
protected void setProjectId(String projectId) { | ||
if (NO_SITE.equals(projectId) || projectId == null) { | ||
return; | ||
} | ||
this.projectId = projectId; | ||
initializeCloudProject(); | ||
} | ||
|
||
private void setToolRoot(String toolRoot) { | ||
protected void setToolRoot(String toolRoot) { | ||
schemaBase = new File(toolRoot, SCHEMA_BASE_PATH); | ||
File[] schemaFiles = schemaBase.listFiles(file -> file.getName().endsWith(SCHEMA_SUFFIX)); | ||
for (File schemaFile : Objects.requireNonNull(schemaFiles)) { | ||
|
@@ -705,6 +723,9 @@ private void loadSiteMetadata() { | |
|
||
File siteMetadataFile = new File(siteDir, SITE_METADATA_JSON); | ||
try (InputStream targetStream = new FileInputStream(siteMetadataFile)) { | ||
// At this time, do not validate the Metadata schema because, by its nature of being | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this comment -- it says "do not validate" and then the code right after is validate()... ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but we ignore the return value, the report. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still confused -- if it's ignored then why is it calling validate()? Do you mean something like "At this time, only validate the structure of the Metadata schema because it's only a partial overlay, and defer any in-depth validation until after merge?" |
||
// a partial overlay on each device Metadata, this Metadata will likely be incomplete | ||
// and fail validation. | ||
schemas.get(METADATA_JSON).validate(OBJECT_MAPPER.readTree(targetStream)); | ||
} catch (FileNotFoundException e) { | ||
return; | ||
|
@@ -720,6 +741,10 @@ private void loadSiteMetadata() { | |
} | ||
} | ||
|
||
protected Map<String, JsonSchema> getSchemas() { | ||
return schemas; | ||
} | ||
|
||
class RelativeDownloader implements URIDownloader { | ||
|
||
@Override | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not sure why this is static? The general adage with static methods for Java is to avoid them unless they need to be static for some reason. I think this comes down to the issue that you can't mock/override a static method, so it makes future testing/extension a problem sometimes. Not likely in this case, but just on general practice. E.g., I would expect this to be just a regular method on the Registrar class (no need to pass in the explicit parameter). Anyway, that's mainly the Java-style that I've always used, but it's been so long that I'm not 100% sure where it came from. I checked the Java style guide and it doesn't say anything about it. Doesn't really matter -- I also optimize for "least number of words" and if you made it a class method, all references to "registrar." go away reducing the overall footprint of the class -- and there's no world in which you would call this method without a registrar class!