Implementing HTTP API

The handler are empty for now, but all the routing and infrastructure is
in place.

The next steps is to wire in the query parser & index libraries.
This commit is contained in:
2019-05-03 12:30:14 +02:00
committed by Lionel Sambuc
parent 75e6dfc71a
commit fb0d65b4b8
18 changed files with 1466 additions and 779 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
.*
!.gitignore
/target
**/*.rs.bk

14
.idea/ServiceTest.iml generated
View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../service_test">
<sourceFolder url="file://$MODULE_DIR$/../service_test/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../service_test/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../service_test/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/../service_test/benches" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/../service_test/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

4
.idea/encodings.xml generated
View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

14
.idea/mercator_service.iml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/../service_test/.idea/ServiceTest.iml" filepath="$PROJECT_DIR$/../service_test/.idea/ServiceTest.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/mercator_service.iml" filepath="$PROJECT_DIR$/.idea/mercator_service.iml" />
</modules>
</component>
</project>

638
.idea/workspace.xml generated
View File

@@ -12,11 +12,7 @@
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="b0ee3aed-2536-4f3a-8c92-83bfe0a44195" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/src/solr_api/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
<list default="true" id="4efa641e-9b05-442b-ba82-4d7003bc775c" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
@@ -28,163 +24,45 @@
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="DatabaseView">
<option name="SHOW_INTERMEDIATE" value="true" />
<option name="GROUP_DATA_SOURCES" value="true" />
<option name="GROUP_SCHEMA" value="true" />
<option name="GROUP_CONTENTS" value="false" />
<option name="SORT_POSITIONED" value="false" />
<option name="SHOW_EMPTY_GROUPS" value="false" />
<option name="AUTO_SCROLL_FROM_SOURCE" value="false" />
<option name="HIDDEN_KINDS">
<set />
</option>
<expand />
<select />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<leaf>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
<caret line="20" selection-start-line="20" selection-end-line="20" />
<state relative-caret-position="330">
<caret line="22" column="2" selection-start-line="22" selection-start-column="2" selection-end-line="22" selection-end-column="2" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/solr_api/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="577">
<caret line="40" selection-start-line="40" selection-end-line="40" />
<folding>
<element signature="e#2490#2808#0" />
<element signature="e#3600#3745#0" />
<element signature="e#4720#4721#0" expanded="true" />
<element signature="e#4750#4751#0" expanded="true" />
<element signature="e#4109#4111#0" />
<element signature="e#5113#5114#0" expanded="true" />
<element signature="e#5147#5148#0" expanded="true" />
<element signature="e#5198#5199#0" expanded="true" />
<element signature="e#5257#5258#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/solr_api/admin.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="3840">
<caret line="340" column="35" selection-start-line="340" selection-start-column="35" selection-end-line="340" selection-end-column="35" />
<folding>
<element signature="e#46#592#0" />
<element signature="e#594#1142#0" />
<element signature="e#1144#1692#0" />
<element signature="e#1694#2242#0" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/solr_api/select.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Rust File" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>CoresAnswer</find>
<find>cores::</find>
<find>ResponseHeader</find>
<find>Requ</find>
<find>params</find>
<find>type</find>
<find>generate_context</find>
<find>builtin</find>
<find>BasicContext</find>
<find>set_</find>
<find>set_params</find>
<find>generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context|generate_context</find>
<find>resolve</find>
<find>context</find>
<find>MultiValued</find>
<find>get&lt;</find>
<find>cookie</find>
<find>K: Hash</find>
<find>HasQueryParams</find>
<find>lock</find>
<find>i32</find>
</findStrings>
<replaceStrings>
<replace>cores::Response</replace>
<replace>HeaderResponse</replace>
<replace>MultiValuedRequestsContext</replace>
<replace>Ctx</replace>
<replace>K: Hash + Eq</replace>
<replace>HasQueryParams&lt;K, V&gt;</replace>
<replace>SharedState</replace>
</replaceStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/src/solr_rest_api.rs" />
<option value="$PROJECT_DIR$/src/service.rs" />
<option value="$PROJECT_DIR$/src/solr_api/query.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/testing/mod.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/hyper_server.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/multi_valued_context.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/app.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/http.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/route_parser.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/server.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/server.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/request.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/send.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/context.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/basic_hyper_context.rs" />
<option value="$PROJECT_DIR$/../Thruster/src/builtins/basic_context.rs" />
<option value="$PROJECT_DIR$/../Thruster/examples/hello_world/context.rs" />
<option value="$PROJECT_DIR$/../Thruster/examples/hyper_most_basic.rs" />
<option value="$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/builtins/basic_context.rs" />
<option value="$PROJECT_DIR$/src/solr_api/admin.rs" />
<option value="$PROJECT_DIR$/src/solr_api/select.rs" />
<option value="$PROJECT_DIR$/src/solr_api/context.rs" />
<option value="$PROJECT_DIR$/src/solr_api/query_params.rs" />
<option value="$PROJECT_DIR$/src/solr_api/service.rs" />
<option value="$PROJECT_DIR$/src/solr_api.rs" />
<option value="$PROJECT_DIR$/Cargo.toml" />
<option value="$PROJECT_DIR$/src/solr_api/tests.rs" />
<option value="$PROJECT_DIR$/src/solr_api/test.rs" />
<option value="$PROJECT_DIR$/src/solr_api/mod.rs" />
<option value="$PROJECT_DIR$/src/main.rs" />
</list>
</option>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="aMXmNNVU" />
<option name="directoryName" value="VyoxinUO" />
</component>
<component name="ProjectConfigurationFiles">
<option name="files">
<list>
<option value="$PROJECT_DIR$/.idea/mercator_service.iml" />
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="552" />
<option name="y" value="44" />
<option name="x" value="1637" />
<option name="y" value="47" />
<option name="width" value="1733" />
<option name="height" value="1282" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
@@ -194,19 +72,26 @@
<subPane>
<expand>
<path>
<item name="service_test" type="b2602c69:ProjectViewProjectNode" />
<item name="service_test" type="462c0819:PsiDirectoryNode" />
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="service_test" type="b2602c69:ProjectViewProjectNode" />
<item name="service_test" type="462c0819:PsiDirectoryNode" />
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="service_test" type="b2602c69:ProjectViewProjectNode" />
<item name="service_test" type="462c0819:PsiDirectoryNode" />
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="solr_api" type="462c0819:PsiDirectoryNode" />
<item name="rest_api" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="rest_api" type="462c0819:PsiDirectoryNode" />
<item name="generated" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
@@ -216,17 +101,9 @@
</panes>
</component>
<component name="PropertiesComponent">
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
<property name="org.rust.hideToolchainNotifications" value="true" />
<property name="restartRequiresConfirmation" value="false" />
<property name="settings.editor.selected.configurable" value="dynamic.analysis.tools.valgrind" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/solr_api" />
</key>
<property name="settings.editor.selected.configurable" value="editor.preferences.import" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
@@ -241,29 +118,9 @@
</option>
</component>
<component name="RunManager" selected="Cargo Command.run-debug">
<configuration name="Test solr_api::tests" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true">
<configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package service_test --bin service_test solr_api::tests" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<method v="2" />
</configuration>
<configuration name="Test solr_api::tests::default_no_path" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package service_test --bin service_test solr_api::tests::default_no_path -- --exact" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<method v="2" />
</configuration>
<configuration name="Test solr_api::tests::default_slash" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package service_test --bin service_test solr_api::tests::default_slash -- --exact" />
<option name="command" value="build" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />
@@ -299,7 +156,7 @@
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs>
<env name="RUST_LOG" value="service_test=trace" />
<env name="RUST_LOG" value="mercator_service=trace" />
</envs>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="build-debug" run_configuration_type="CargoCommandRunConfiguration" />
@@ -313,447 +170,56 @@
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs>
<env name="RUST_LOG" value="service_test=info" />
<env name="RUST_LOG" value="mercator_service=trace" />
</envs>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="build" run_configuration_type="CargoCommandRunConfiguration" />
</method>
</configuration>
<list>
<item itemvalue="Cargo Command.build-debug" />
<item itemvalue="Cargo Command.run-debug" />
<item itemvalue="Cargo Command.build" />
<item itemvalue="Cargo Command.run" />
<item itemvalue="Cargo Command.Test solr_api::tests" />
<item itemvalue="Cargo Command.Test solr_api::tests::default_no_path" />
<item itemvalue="Cargo Command.Test solr_api::tests::default_slash" />
<item itemvalue="Cargo Command.run-debug" />
<item itemvalue="Cargo Command.build-debug" />
<item itemvalue="Cargo Command.build" />
</list>
<recent_temporary>
<list>
<item itemvalue="Cargo Command.Test solr_api::tests" />
<item itemvalue="Cargo Command.Test solr_api::tests::default_slash" />
<item itemvalue="Cargo Command.Test solr_api::tests::default_no_path" />
</list>
</recent_temporary>
</component>
<component name="RustProjectSettings">
<option name="runRustfmtOnSave" value="true" />
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
<option name="version" value="2" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TestHistory">
<history-entry file="Test_solr_api__tests__default_no_path - 2019.05.02 at 14h 53m 55s.xml">
<configuration name="Test solr_api::tests::default_no_path" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests__default_slash - 2019.05.02 at 14h 54m 41s.xml">
<configuration name="Test solr_api::tests::default_slash" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 14h 54m 50s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 14h 55m 27s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 15h 00m 22s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 15h 12m 33s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 15h 13m 50s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.02 at 15h 14m 39s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.03 at 09h 23m 38s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
<history-entry file="Test_solr_api__tests - 2019.05.03 at 09h 39m 37s.xml">
<configuration name="Test solr_api::tests" configurationId="CargoCommandRunConfiguration" />
</history-entry>
</component>
<component name="ToolWindowManager">
<frame x="552" y="44" width="1733" height="1282" extended-state="0" />
<frame x="1637" y="47" width="1733" height="1282" extended-state="0" />
<editor active="true" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" sideWeight="0.24310777" visible="true" weight="0.26729745" />
<window_info id="Structure" order="1" sideWeight="0.7568922" side_tool="true" visible="true" weight="0.26729745" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info id="Favorites" side_tool="true" />
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.24955648" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Tool Output" />
<window_info anchor="bottom" id="Database Changes" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="ANTLR Preview" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" visible="true" weight="0.32941177" />
<window_info anchor="bottom" x="2454" y="149" width="972" height="1064" id="Run" order="2" sideWeight="0.49970433" type="WINDOWED" visible="true" weight="0.35210085" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info active="true" anchor="bottom" id="Run" order="2" visible="true" weight="0.32941177" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Database Changes" order="7" />
<window_info anchor="bottom" id="Version Control" order="8" />
<window_info anchor="bottom" id="Event Log" order="9" sideWeight="0.5002957" side_tool="true" weight="0.39327732" />
<window_info anchor="right" id="Cargo" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Cargo" order="3" weight="0.32998225" />
<window_info anchor="right" id="Database" order="4" weight="0.32998225" />
</layout>
<layout-to-restore>
<window_info id="Favorites" order="0" side_tool="true" />
<window_info content_ui="combo" id="Project" order="1" visible="true" weight="0.25" />
<window_info id="Structure" order="2" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Version Control" order="0" />
<window_info anchor="bottom" id="Event Log" order="1" side_tool="true" />
<window_info anchor="bottom" id="Database Changes" order="2" />
<window_info anchor="bottom" id="Message" order="3" />
<window_info anchor="bottom" id="Find" order="4" />
<window_info active="true" anchor="bottom" id="Run" order="5" visible="true" weight="0.33094555" />
<window_info anchor="bottom" id="Debug" order="6" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="7" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="8" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="9" />
<window_info anchor="right" id="Cargo" order="0" />
<window_info anchor="right" id="Database" order="1" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="2" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
</layout-to-restore>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/../Thruster/examples/hello_world/context.rs" />
<entry file="file://$PROJECT_DIR$/../Thruster/src/builtins/query_params.rs" />
<entry file="file://$PROJECT_DIR$/../Thruster/src/builtins/basic_context.rs" />
<entry file="file://$PROJECT_DIR$/../Thruster/src/context.rs" />
<entry file="file://$PROJECT_DIR$/Cargo.lock">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/builtins/server.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1710">
<caret line="114" column="9" lean-forward="true" selection-start-line="114" selection-start-column="9" selection-end-line="114" selection-end-column="9" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/route_tree/node.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-172">
<caret line="77" column="16" lean-forward="true" selection-start-line="77" selection-start-column="16" selection-end-line="77" selection-end-column="16" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/route_tree/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="863">
<caret line="64" column="34" selection-start-line="64" selection-start-column="34" selection-end-line="64" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.rustup/toolchains/beta-x86_64-apple-darwin/lib/rustlib/src/rust/src/liballoc/str.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="387">
<caret line="194" column="7" selection-start-line="194" selection-start-column="7" selection-end-line="194" selection-end-column="7" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/lib.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="405">
<caret line="27" selection-start-line="27" selection-end-line="27" selection-end-column="17" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/builtins/basic_context.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="91">
<caret line="7" column="8" lean-forward="true" selection-start-line="7" selection-start-column="8" selection-end-line="7" selection-end-column="8" />
<folding>
<element signature="e#1578#1579#0" expanded="true" />
<element signature="e#1606#1607#0" expanded="true" />
<element signature="e#3798#3799#0" expanded="true" />
<element signature="e#3830#3831#0" expanded="true" />
<element signature="e#4017#4018#0" expanded="true" />
<element signature="e#4045#4046#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/builtins/query_params.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="240">
<caret line="16" column="36" lean-forward="true" selection-start-line="16" selection-start-column="36" selection-end-line="16" selection-end-column="36" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/builtins/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="45">
<caret line="3" column="13" selection-start-line="3" selection-start-column="13" selection-end-line="3" selection-end-column="13" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.7.1/src/lib.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="285">
<caret line="19" column="56" selection-start-line="19" selection-start-column="42" selection-end-line="19" selection-end-column="56" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/examples/multicontext.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-103" />
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/examples/run_test/main.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/examples/hello_world/context.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-237">
<caret line="12" column="23" lean-forward="true" selection-start-line="12" selection-start-column="23" selection-end-line="12" selection-end-column="23" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/middleware.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="222">
<caret line="26" column="78" selection-start-line="26" selection-start-column="78" selection-end-line="26" selection-end-column="78" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.25/src/stream/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="312">
<caret line="606" column="7" lean-forward="true" selection-start-line="606" selection-start-column="7" selection-end-line="606" selection-end-column="7" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/extensions.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-228">
<caret line="36" column="11" selection-start-line="36" selection-start-column="11" selection-end-line="36" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/request.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="423">
<caret line="178" column="24" selection-start-line="178" selection-start-column="24" selection-end-line="178" selection-end-column="24" />
<folding>
<element signature="e#11750#11751#0" expanded="true" />
<element signature="e#11782#11783#0" expanded="true" />
<element signature="e#12465#12466#0" expanded="true" />
<element signature="e#12494#12495#0" expanded="true" />
<element signature="e#13941#13942#0" expanded="true" />
<element signature="e#13974#13975#0" expanded="true" />
<element signature="e#14783#14784#0" expanded="true" />
<element signature="e#14819#14820#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.12.25/src/body/body.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-350">
<caret line="22" column="11" selection-start-line="22" selection-start-column="11" selection-end-line="22" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.12.25/src/server/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="249">
<caret line="80" column="11" selection-start-line="80" selection-start-column="11" selection-end-line="80" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/uri/path.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="80">
<caret line="11" column="11" selection-start-line="11" selection-start-column="11" selection-end-line="11" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/examples/hello_world/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="225">
<caret line="15" column="3" selection-start-line="14" selection-end-line="15" selection-end-column="3" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/examples/most_basic.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/app.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="412">
<caret line="119" column="15" selection-start-line="119" selection-start-column="15" selection-end-line="119" selection-end-column="15" />
<folding>
<element signature="e#3505#3506#0" expanded="true" />
<element signature="e#3543#3544#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/thruster-0.6.3/src/server.rs">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="6" selection-start-column="6" selection-end-column="6" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/uri/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-652">
<caret line="825" column="3" selection-start-line="825" selection-start-column="3" selection-end-line="825" selection-end-column="3" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/../service_test.bk/src/solr_api/query_params.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="100">
<caret line="15" selection-start-line="9" selection-end-line="15" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/response.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="222">
<caret line="177" column="11" selection-start-line="177" selection-start-column="11" selection-end-line="177" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-0.7.9/src/system.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="307">
<caret line="165" column="11" selection-start-line="165" selection-start-column="11" selection-end-line="165" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/src/liballoc/sync.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="250">
<caret line="401" column="30" selection-start-line="401" selection-start-column="30" selection-end-line="401" selection-end-column="30" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1318">
<caret line="24" column="1" lean-forward="true" selection-start-line="24" selection-start-column="1" selection-end-line="25" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api.bk/mod.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/context.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-6702" />
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/service.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/Cargo.toml">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="36">
<caret line="8" column="16" selection-start-line="8" selection-start-column="16" selection-end-line="8" selection-end-column="16" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/test.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-0.7.19/src/server/http.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="585">
<caret line="314" column="11" selection-start-line="314" selection-start-column="11" selection-end-line="314" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-0.7.19/src/application.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="406">
<caret line="562" column="75" lean-forward="true" selection-start-line="562" selection-start-column="30" selection-end-line="562" selection-end-column="75" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-0.7.19/src/client/request.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="402">
<caret line="293" column="15" lean-forward="true" selection-start-line="293" selection-start-column="15" selection-end-line="293" selection-end-column="15" />
<folding>
<element signature="e#22650#22651#0" expanded="true" />
<element signature="e#22687#22688#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/url-1.7.2/src/lib.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="681">
<caret line="189" column="34" lean-forward="true" selection-start-line="189" selection-start-column="34" selection-end-line="189" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-web-0.7.19/src/test.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="241">
<caret line="114" column="57" selection-start-line="114" selection-start-column="46" selection-end-line="114" selection-end-column="57" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/http-0.1.16/src/status.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="387">
<caret line="42" column="11" selection-start-line="42" selection-start-column="11" selection-end-line="42" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/query_params.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/select.rs">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/src/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
<caret line="20" selection-start-line="20" selection-end-line="20" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="577">
<caret line="40" selection-start-line="40" selection-end-line="40" />
<folding>
<element signature="e#2490#2808#0" />
<element signature="e#3600#3745#0" />
<element signature="e#4720#4721#0" expanded="true" />
<element signature="e#4750#4751#0" expanded="true" />
<element signature="e#4109#4111#0" />
<element signature="e#5113#5114#0" expanded="true" />
<element signature="e#5147#5148#0" expanded="true" />
<element signature="e#5198#5199#0" expanded="true" />
<element signature="e#5257#5258#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/solr_api/admin.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="3840">
<caret line="340" column="35" selection-start-line="340" selection-start-column="35" selection-end-line="340" selection-end-column="35" />
<folding>
<element signature="e#46#592#0" />
<element signature="e#594#1142#0" />
<element signature="e#1144#1692#0" />
<element signature="e#1694#2242#0" />
</folding>
<state relative-caret-position="330">
<caret line="22" column="2" selection-start-line="22" selection-start-column="2" selection-end-line="22" selection-end-column="2" />
</state>
</provider>
</entry>

View File

@@ -11,7 +11,7 @@ extern crate pretty_env_logger;
use std::sync::Arc;
use std::sync::RwLock;
mod solr_api;
mod rest_api;
/*
fn into_bool(string: &str) -> bool {
@@ -20,7 +20,7 @@ fn into_bool(string: &str) -> bool {
*/
fn main() {
std::env::set_var("RUST_LOG", "info");
// std::env::set_var("RUST_LOG", "info");
pretty_env_logger::init();
//TODO Retrieve from environment values, with fall back to defaults if unset.
@@ -28,5 +28,5 @@ fn main() {
let base = "/spatial-search";
let port = 8888;
solr_api::run(hostname, port, base, Arc::new(RwLock::new(0)));
rest_api::run(hostname, port, base, Arc::new(RwLock::new(0)));
}

41
src/rest_api/actions.rs Normal file
View File

@@ -0,0 +1,41 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, HttpResponse, Path, Result};
pub fn health(_req: &HttpRequest<AppState>) -> HttpResponse {
HttpResponse::Ok().finish()
}
pub fn query(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("query Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
#[test]
fn health() {
let ep = get_path("/health".into());
expect_200(http::Method::GET, ep.clone());
expect_400(http::Method::POST, ep.clone());
expect_400(http::Method::PUT, ep.clone());
expect_400(http::Method::PATCH, ep.clone());
expect_400(http::Method::DELETE, ep.clone());
}
#[test]
fn query() {
let ep = get_path("/query".into());
expect_200(http::Method::POST, ep.clone());
expect_422(http::Method::POST, ep.clone());
expect_400(http::Method::GET, ep.clone());
expect_400(http::Method::PUT, ep.clone());
expect_400(http::Method::PATCH, ep.clone());
expect_400(http::Method::DELETE, ep.clone());
}
}

85
src/rest_api/dataset.rs Normal file
View File

@@ -0,0 +1,85 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
/*
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const INSTANCE_EXISTS: &str = "/datasets/42";
const INSTANCE_INVALID: &str = "/datasets/21";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_200(
http::Method::PUT,
get_path(INSTANCE_INVALID),
"".to_string(),
);
}
#[test]
fn patch() {
json::expect_200(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
json::expect_422(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
}
#[test]
fn get() {
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
}
#[test]
fn delete() {
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
}
#[test]
fn post() {
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
}
}

81
src/rest_api/datasets.rs Normal file
View File

@@ -0,0 +1,81 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn put(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
/*
pub fn get(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}*/
pub fn patch(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const COLLECTION: &str = "/datasets";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(http::Method::POST, get_path(COLLECTION));
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
expect_400(http::Method::POST, get_path(COLLECTION));
}
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PUT, get_path(COLLECTION));
}
#[test]
fn patch() {
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PATCH, get_path(COLLECTION));
}
#[test]
fn delete() {
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
expect_400(http::Method::DELETE, get_path(COLLECTION));
}
#[test]
fn get() {
expect_400(http::Method::GET, get_path(COLLECTION));
}
}

87
src/rest_api/default.rs Normal file
View File

@@ -0,0 +1,87 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Result};
pub fn page_400(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("Default 400 Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn page_400_no_state(_req: &HttpRequest) -> Result<fs::NamedFile> {
info!("Default 400 Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn page_404(_req: &HttpRequest) -> Result<fs::NamedFile> {
info!("Default 404 (no state) Triggered!");
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::sync::RwLock;
use actix_web::http;
use actix_web::test::TestRequest;
#[test]
fn page_400() {
let response = TestRequest::with_state(AppState {
shared: Arc::new(RwLock::new(0)),
})
.run(&super::page_400)
.unwrap();
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
}
#[test]
fn page_400_no_state() {
let response = TestRequest::default()
.run(&super::page_400_no_state)
.unwrap();
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
}
#[test]
fn page_404() {
let response = TestRequest::default().run(&super::page_404).unwrap();
assert_eq!(response.status(), http::StatusCode::NOT_FOUND);
}
}
#[cfg(test)]
mod routing {
use super::super::tests::*;
#[test]
fn default_no_path() {
expect_404(http::Method::GET, "".into());
}
#[test]
fn default_slash() {
expect_404(http::Method::GET, "/".into());
expect_404(http::Method::GET, "//".into());
expect_404(http::Method::GET, "/ /".into());
expect_404(http::Method::GET, "/ //".into());
expect_404(http::Method::GET, "// ".into());
}
#[test]
fn default_invalid_prefix() {
expect_404(http::Method::GET, "/test".into());
expect_404(http::Method::GET, format!("{}test", PREFIX));
}
#[test]
fn default_prefix_no_slash() {
expect_400(http::Method::GET, PREFIX.into());
}
#[test]
fn default_prefix_final_slash() {
expect_400(http::Method::GET, format!("{}/", PREFIX));
}
}

540
src/rest_api/generated/models.rs Executable file
View File

@@ -0,0 +1,540 @@
#![allow(unused_imports, unused_qualifications, unused_extern_crates)]
extern crate chrono;
extern crate uuid;
use serde::ser::Serializer;
use std::collections::HashMap;
use models;
use swagger;
/// Defines the properties of an axis. The origin and unit vectors or defined within the universe space, but this does NOT imply a linear conversion is possible, this only provide anchoring of the axis as well as its absolute direction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Axis {
/// Unit of the values, on this axis, for example [mm], [s], [um].
#[serde(rename = "measurement_unit")]
#[serde(skip_serializing_if="Option::is_none")]
pub measurement_unit: Option<String>,
#[serde(rename = "coordinates")]
#[serde(skip_serializing_if="Option::is_none")]
pub coordinates: Option<models::ValidNumbersOnThisAxis>,
#[serde(rename = "origin")]
#[serde(skip_serializing_if="Option::is_none")]
pub origin: Option<Vec<models::Point>>,
#[serde(rename = "unit_vector")]
#[serde(skip_serializing_if="Option::is_none")]
pub unit_vector: Option<Vec<models::Point>>,
}
impl Axis {
pub fn new() -> Axis {
Axis {
measurement_unit: None,
coordinates: None,
origin: None,
unit_vector: None,
}
}
}
/// Collection of Spatial Objects, stored in one or more Reference Spaces.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DataSet {
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
#[serde(rename = "version")]
#[serde(skip_serializing_if="Option::is_none")]
pub version: Option<String>,
/// Scale factors used to generate less precise, coarser indexes in order to speed up queries over large volumes of the space. Values are expressed as powers of two, in the range [0;n]. For each scale, a whole vector providing values for each axis MUST be provided. Values, which are equal, and whose coordinates gets merged are merged as well, to reduce the number of results. Distinct values whose coordinates are merged are recorded, thus allowing the user to move from one scale factor to another, with a finer resolution smoothly.
#[serde(rename = "scales")]
#[serde(skip_serializing_if="Option::is_none")]
pub scales: Option<Vec<Vec<f64>>>,
}
impl DataSet {
pub fn new() -> DataSet {
DataSet {
name: None,
version: None,
scales: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Datasets {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::DataSet>>,
}
impl Datasets {
pub fn new() -> Datasets {
Datasets {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Datasets1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl Datasets1 {
pub fn new() -> Datasets1 {
Datasets1 {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Filters {
#[serde(rename = "filter")]
#[serde(skip_serializing_if="Option::is_none")]
pub filter: Option<String>,
#[serde(rename = "ids_only")]
#[serde(skip_serializing_if="Option::is_none")]
pub ids_only: Option<bool>,
}
impl Filters {
pub fn new() -> Filters {
Filters {
filter: None,
ids_only: Some(false),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse200 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::Space>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::Space>,
}
impl InlineResponse200 {
pub fn new() -> InlineResponse200 {
InlineResponse200 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse2001 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::DataSet>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::DataSet>,
}
impl InlineResponse2001 {
pub fn new() -> InlineResponse2001 {
InlineResponse2001 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse2002 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::SpatialObject>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::SpatialObject>,
}
impl InlineResponse2002 {
pub fn new() -> InlineResponse2002 {
InlineResponse2002 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate {
/// Identifier or name of the instance to update.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate {
pub fn new() -> PartialUpdate {
PartialUpdate {
name: None,
attribute: None,
value: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate1 {
/// Identifier or name of the instance to update.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate1 {
pub fn new() -> PartialUpdate1 {
PartialUpdate1 {
name: None,
attribute: None,
value: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate2 {
/// Identifier or name of the instance to update.
#[serde(rename = "id")]
#[serde(skip_serializing_if="Option::is_none")]
pub id: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate2 {
pub fn new() -> PartialUpdate2 {
PartialUpdate2 {
id: None,
attribute: None,
value: None,
}
}
}
/// One valid value for each axes of the reference space this point is used in.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Point(Vec<Number>);
impl ::std::convert::From<Vec<Number>> for Point {
fn from(x: Vec<Number>) -> Self {
Point(x)
}
}
impl ::std::convert::From<Point> for Vec<Number> {
fn from(x: Point) -> Self {
x.0
}
}
impl ::std::iter::FromIterator<Number> for Point {
fn from_iter<U: IntoIterator<Item=Number>>(u: U) -> Self {
Point(Vec::<Number>::from_iter(u))
}
}
impl ::std::iter::IntoIterator for Point {
type Item = Number;
type IntoIter = ::std::vec::IntoIter<Number>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> ::std::iter::IntoIterator for &'a Point {
type Item = &'a Number;
type IntoIter = ::std::slice::Iter<'a, Number>;
fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter()
}
}
impl<'a> ::std::iter::IntoIterator for &'a mut Point {
type Item = &'a mut Number;
type IntoIter = ::std::slice::IterMut<'a, Number>;
fn into_iter(self) -> Self::IntoIter {
(&mut self.0).into_iter()
}
}
impl ::std::ops::Deref for Point {
type Target = Vec<Number>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::ops::DerefMut for Point {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Properties tied to a shape, in other words properties valid for the whole content of the shape.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Properties {
#[serde(rename = "id")]
#[serde(skip_serializing_if="Option::is_none")]
pub id: Option<String>,
/// Label defining the kind of the spatial object.
#[serde(rename = "type")]
#[serde(skip_serializing_if="Option::is_none")]
pub _type: Option<String>,
}
impl Properties {
pub fn new() -> Properties {
Properties {
id: None,
_type: None,
}
}
}
/// Geometric shape defined in a reference space.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Shape {
/// Name of the shape class described by the vertices, this can be used for specific types to reduce the number of vertices required to define the shape.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "type")]
#[serde(skip_serializing_if="Option::is_none")]
pub _type: Option<String>,
/// List of vertices composing the contour of the shape.
#[serde(rename = "vertices")]
#[serde(skip_serializing_if="Option::is_none")]
pub vertices: Option<Vec<models::Point>>,
/// Name of a valid reference space. This is the space in which the vertices are defined
#[serde(rename = "space")]
#[serde(skip_serializing_if="Option::is_none")]
pub space: Option<String>,
}
impl Shape {
pub fn new() -> Shape {
Shape {
_type: None,
vertices: None,
space: None,
}
}
}
/// Definition of a space, in which objects are described.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Space {
/// Unique Id for the space, which can also be used to generate a link to the user documentation describing the space, explaining the semantic meaning of the values stored, as well as the definitions of the axes.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// The order of the axes matter and MUST be kept, as this is also linked to the definition found in the documentation. Coordinate of a point MUST always be expressed using the same order as defined here.
#[serde(rename = "axes")]
#[serde(skip_serializing_if="Option::is_none")]
pub axes: Option<Vec<models::Axis>>,
}
impl Space {
pub fn new() -> Space {
Space {
name: None,
axes: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Spaces {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::Space>>,
}
impl Spaces {
pub fn new() -> Spaces {
Spaces {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Spaces1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl Spaces1 {
pub fn new() -> Spaces1 {
Spaces1 {
list: None,
}
}
}
/// Collection of positions in a space, which share a common set of properties.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObject {
/// List of shapes, overlapping or not, which define the whole space covered by this spatial object.
#[serde(rename = "shape")]
#[serde(skip_serializing_if="Option::is_none")]
pub shape: Option<Vec<models::Shape>>,
#[serde(rename = "properties")]
#[serde(skip_serializing_if="Option::is_none")]
pub properties: Option<models::Properties>,
}
impl SpatialObject {
pub fn new() -> SpatialObject {
SpatialObject {
shape: None,
properties: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObjects {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::SpatialObject>>,
}
impl SpatialObjects {
pub fn new() -> SpatialObjects {
SpatialObjects {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObjects1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl SpatialObjects1 {
pub fn new() -> SpatialObjects1 {
SpatialObjects1 {
list: None,
}
}
}
/// Definition of the valid coordinate values which can be used on this axis.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidNumbersOnThisAxis {
/// Valid numbers as defined by the usual mathematical sets, for example N=Natural, Z=Integers, Q=Rational, R=Real.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "set")]
#[serde(skip_serializing_if="Option::is_none")]
pub set: Option<String>,
#[serde(rename = "minimum")]
#[serde(skip_serializing_if="Option::is_none")]
pub minimum: Option<f32>,
#[serde(rename = "maximum")]
#[serde(skip_serializing_if="Option::is_none")]
pub maximum: Option<f32>,
#[serde(rename = "steps")]
#[serde(skip_serializing_if="Option::is_none")]
pub steps: Option<f64>,
}
impl ValidNumbersOnThisAxis {
pub fn new() -> ValidNumbersOnThisAxis {
ValidNumbersOnThisAxis {
set: None,
minimum: None,
maximum: None,
steps: None,
}
}
}

227
src/rest_api/mod.rs Normal file
View File

@@ -0,0 +1,227 @@
use std::sync::Arc;
use std::sync::RwLock;
use actix_web::http::Method;
use actix_web::server::{HttpHandler, HttpHandlerTask};
use actix_web::{pred, server, App};
pub type SharedState = i32;
// Application shared state
pub struct AppState {
shared: Arc<RwLock<SharedState>>,
}
/* EXAMPLE FOR STATE USAGE
// simple handle
fn index(req: &HttpRequest<AppState>) -> HttpResponse {
println!("{:?}", req);
{
// So that we release ASAP the exclusive lock.
*(req.state().shared.write().unwrap()) += 1;
}
HttpResponse::BadRequest().body(format!(
"Num of requests: {}",
req.state().shared.read().unwrap()
))
}
*/
mod actions;
mod space;
mod spaces;
mod dataset;
mod datasets;
mod spatial_object;
mod spatial_objects;
mod default;
fn get_app(
prefix: &'static str,
state: Arc<RwLock<SharedState>>,
) -> Vec<Box<HttpHandler<Task = Box<HttpHandlerTask>>>> {
vec![
App::with_state(AppState { shared: state })
.prefix(format!("{}", prefix))
// ACTIONS -------------------------------------------------------------------
.resource("/health", |r| {
r.method(Method::GET).f(actions::health);
r.route()
.filter(pred::Not(pred::Get()))
.f(default::page_400);
})
.resource("/queries", |r| {
r.method(Method::POST).f(actions::query);
r.route()
.filter(pred::Not(pred::Post()))
.f(default::page_400);
})
// SPACES -------------------------------------------------------------------
.resource("/spaces", |r| {
r.method(Method::POST).f(spaces::post);
r.method(Method::PUT).f(spaces::put);
r.method(Method::PATCH).f(spaces::patch);
r.method(Method::DELETE).f(spaces::delete);
})
.resource("/spaces/{name}", |r| {
r.method(Method::PUT).with(space::put);
r.method(Method::PATCH).with(space::patch);
r.method(Method::GET).with(space::get);
r.method(Method::DELETE).with(space::delete);
})
// DATASETS -------------------------------------------------------------------
.resource("/datasets", |r| {
r.method(Method::POST).f(&datasets::post);
r.method(Method::PUT).f(&datasets::put);
r.method(Method::PATCH).f(&datasets::patch);
r.method(Method::DELETE).f(&datasets::delete);
})
.resource("/datasets/{name}", |r| {
r.method(Method::PUT).with(dataset::put);
r.method(Method::GET).with(dataset::get);
r.method(Method::PATCH).with(dataset::patch);
r.method(Method::DELETE).with(dataset::delete);
})
// SPATIAL OBJECTS -------------------------------------------------------------------
.resource("/dataset/{name}/spatial_objects", |r| {
r.method(Method::POST).with(spatial_objects::post);
r.method(Method::PUT).with(spatial_objects::put);
r.method(Method::PATCH).with(spatial_objects::patch);
r.method(Method::DELETE).with(spatial_objects::delete);
})
.resource("/dataset/{name}/spatial_objects/{id}", |r| {
r.method(Method::PUT).with(spatial_object::put);
r.method(Method::GET).with(spatial_object::get);
r.method(Method::PATCH).with(spatial_object::patch);
r.method(Method::DELETE).with(spatial_object::delete);
})
// DEFAULT -------------------------------------------------------------------
.default_resource(|r| {
r.f(default::page_400);
})
.boxed(),
App::new()
.default_resource(|r| {
// 404 for GET request
r.method(Method::GET).f(default::page_404);
// all requests that are not `GET`
r.route()
.filter(pred::Not(pred::Get()))
.f(default::page_400_no_state);
})
.boxed(),
]
}
pub fn run(
host: &'static str,
port: u16,
prefix: &'static str,
state: Arc<RwLock<SharedState>>,
) -> () {
info!("Initializing server...");
let sys = actix::System::new("spatial-search");
server::new(move || get_app(prefix, state.clone()))
.bind(format!("{}:{}", host, port))
.unwrap()
.start();
info!("Started http server: {}:{}{}", host, port, prefix);
let _ = sys.run();
}
#[cfg(test)]
mod tests {
use super::get_app;
use super::{Arc, RwLock, SharedState};
pub use actix_web::http;
pub use actix_web::http::Method;
pub use actix_web::test::TestServer;
pub const PREFIX: &str = "spatial-search";
fn get_start_state() -> Arc<RwLock<SharedState>> {
Arc::new(RwLock::new(0))
}
pub fn get_test_server() -> TestServer {
TestServer::with_factory(move || get_app(PREFIX, get_start_state().clone()))
}
pub fn get_path(path: &str) -> String {
format!("{}{}", PREFIX, path)
}
pub fn expect_200(method: Method, path: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::OK, response.status());
}
pub fn expect_400(method: Method, path: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::BAD_REQUEST, response.status());
}
pub fn expect_404(method: Method, path: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::NOT_FOUND, response.status());
}
pub fn expect_422(method: Method, path: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status());
}
pub mod json {
use super::*;
pub fn expect_200(method: Method, path: String, json: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).json(json).unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::OK, response.status());
}
/*
pub fn expect_400(method: Method, path: String, json: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).json(json).unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::BAD_REQUEST, response.status());
}
*/
pub fn expect_404(method: Method, path: String, json: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).json(json).unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::NOT_FOUND, response.status());
}
pub fn expect_422(method: Method, path: String, json: String) -> () {
let mut srv = get_test_server();
let req = srv.client(method, path.as_str()).json(json).unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status());
}
}
}

85
src/rest_api/space.rs Normal file
View File

@@ -0,0 +1,85 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
/*
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const INSTANCE_EXISTS: &str = "/spaces/42";
const INSTANCE_INVALID: &str = "/spaces/21";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_200(
http::Method::PUT,
get_path(INSTANCE_INVALID),
"".to_string(),
);
}
#[test]
fn patch() {
json::expect_200(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
json::expect_422(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
}
#[test]
fn get() {
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
}
#[test]
fn delete() {
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
}
#[test]
fn post() {
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
}
}

81
src/rest_api/spaces.rs Normal file
View File

@@ -0,0 +1,81 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn put(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
/*
pub fn get(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}*/
pub fn patch(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const COLLECTION: &str = "/spaces";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(http::Method::POST, get_path(COLLECTION));
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
expect_400(http::Method::POST, get_path(COLLECTION));
}
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PUT, get_path(COLLECTION));
}
#[test]
fn patch() {
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PATCH, get_path(COLLECTION));
}
#[test]
fn delete() {
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
expect_400(http::Method::DELETE, get_path(COLLECTION));
}
#[test]
fn get() {
expect_400(http::Method::GET, get_path(COLLECTION));
}
}

View File

@@ -0,0 +1,85 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
/*
pub fn post((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const INSTANCE_EXISTS: &str = "/datasets/42/spatial_objects/42";
const INSTANCE_INVALID: &str = "/datasets/42/spatial_objects/21";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_422(http::Method::PUT, get_path(INSTANCE_EXISTS), "".to_string());
json::expect_200(
http::Method::PUT,
get_path(INSTANCE_INVALID),
"".to_string(),
);
}
#[test]
fn patch() {
json::expect_200(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
json::expect_422(
http::Method::PATCH,
get_path(INSTANCE_EXISTS),
"".to_string(),
);
expect_400(http::Method::PATCH, get_path(INSTANCE_INVALID));
}
#[test]
fn get() {
expect_200(http::Method::GET, get_path(INSTANCE_EXISTS));
expect_404(http::Method::GET, get_path(INSTANCE_INVALID));
}
#[test]
fn delete() {
expect_200(http::Method::DELETE, get_path(INSTANCE_EXISTS));
expect_404(http::Method::DELETE, get_path(INSTANCE_INVALID));
}
#[test]
fn post() {
expect_400(http::Method::POST, get_path(INSTANCE_EXISTS));
expect_400(http::Method::POST, get_path(INSTANCE_INVALID));
}
}

View File

@@ -0,0 +1,82 @@
use super::AppState;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
/*
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
*/
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const COLLECTION: &str = "/datasets/42/spatial_objects";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(http::Method::POST, get_path(COLLECTION));
json::expect_200(http::Method::POST, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::POST, get_path(COLLECTION), "".to_string());
expect_400(http::Method::POST, get_path(COLLECTION));
}
#[test]
fn put() {
json::expect_200(http::Method::PUT, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PUT, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PUT, get_path(COLLECTION));
}
#[test]
fn patch() {
json::expect_200(http::Method::PATCH, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::PATCH, get_path(COLLECTION), "".to_string());
expect_400(http::Method::PATCH, get_path(COLLECTION));
}
#[test]
fn delete() {
json::expect_200(http::Method::DELETE, get_path(COLLECTION), "".to_string());
json::expect_422(http::Method::DELETE, get_path(COLLECTION), "".to_string());
expect_400(http::Method::DELETE, get_path(COLLECTION));
}
#[test]
fn get() {
expect_400(http::Method::GET, get_path(COLLECTION));
}
}

View File

@@ -1,171 +0,0 @@
use std::sync::Arc;
use std::sync::RwLock;
use actix_web::http::{Method, StatusCode};
use actix_web::server::{HttpHandler, HttpHandlerTask};
use actix_web::{fs, pred, server, App, HttpRequest, HttpResponse, Result};
pub type SharedState = i32;
// Application shared state
struct AppState {
/*host: &'static str,
port: u16,
prefix: &'static str,*/
shared: Arc<RwLock<SharedState>>,
}
// simple handle
fn index(req: &HttpRequest<AppState>) -> HttpResponse {
println!("{:?}", req);
{
// So that we release ASAP the exclusive lock.
*(req.state().shared.write().unwrap()) += 1;
}
HttpResponse::BadRequest().body(format!(
"Num of requests: {}",
req.state().shared.read().unwrap()
))
}
/// 400 handler
fn page_400(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
/// 404 handler
fn page_404(_req: &HttpRequest) -> Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
}
fn get_app(
prefix: &'static str,
state: Arc<RwLock<SharedState>>,
) -> Vec<Box<HttpHandler<Task = Box<HttpHandlerTask>>>> {
vec![
App::with_state(AppState { shared: state })
.prefix(format!("{}", prefix))
// register simple handler, handle all methods
.resource("/", |r| r.f(index))
// default
.default_resource(|r| {
// 400 for GET request
r.f(page_400);
})
.boxed(),
App::new()
.default_resource(|r| {
// 404 for GET request
r.method(Method::GET).f(page_404);
// all requests that are not `GET`
r.route()
.filter(pred::Not(pred::Get()))
.f(|_req| HttpResponse::MethodNotAllowed());
})
.boxed(),
]
}
pub fn run(
host: &'static str,
port: u16,
prefix: &'static str,
state: Arc<RwLock<SharedState>>,
) -> () {
info!("Initializing server...");
let sys = actix::System::new("spatial-search");
server::new(move || get_app(prefix, state.clone()))
.bind(format!("{}:{}", host, port))
.unwrap()
.start();
info!("Started http server: {}:{}{}", host, port, prefix);
let _ = sys.run();
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::http;
use actix_web::test::{TestRequest, TestServer};
const PREFIX: &str = "spatial-search";
fn get_test_server() -> TestServer {
let state = Arc::new(RwLock::new(0));
TestServer::with_factory(move || get_app(PREFIX, state.clone()))
}
fn expect_200(path: &str) -> () {
let mut srv = get_test_server();
let req = srv.client(http::Method::GET, path).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::OK, response.status());
}
fn expect_400(path: &str) -> () {
let mut srv = get_test_server();
let req = srv.client(http::Method::GET, path).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::BAD_REQUEST, response.status());
}
fn expect_404(path: &str) -> () {
let mut srv = get_test_server();
let req = srv.client(http::Method::GET, path).finish().unwrap();
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::NOT_FOUND, response.status());
}
#[test]
fn page_400() {
let response = TestRequest::with_state(AppState {
shared: Arc::new(RwLock::new(0)),
})
.run(&super::page_400)
.unwrap();
assert_eq!(response.status(), http::StatusCode::BAD_REQUEST);
}
#[test]
fn page_404() {
let response = TestRequest::default().run(&super::page_404).unwrap();
assert_eq!(response.status(), http::StatusCode::NOT_FOUND);
}
#[test]
fn default_no_path() {
expect_404("");
}
#[test]
fn default_slash() {
expect_404("/");
expect_404("//");
expect_404("/ /");
expect_404("/ //");
expect_404("// ");
}
#[test]
fn default_invalid_prefix() {
expect_404("/test");
expect_404(format!("{}test", PREFIX).as_str());
}
#[test]
fn default_prefix_no_slash() {
expect_400(PREFIX);
}
#[test]
fn default_prefix_final_slash() {
expect_400(format!("{}/", PREFIX).as_str());
}
}