Gisle Aune
6 years ago
32 changed files with 1551 additions and 277 deletions
-
6.idea/misc.xml
-
8.idea/modules.xml
-
6.idea/vcs.xml
-
29.idea/watcherTasks.xml
-
586.idea/workspace.xml
-
217database/mongodb/characters.go
-
51database/mongodb/db.go
-
10database/mongodb/tags.go
-
30go.mod
-
57go.sum
-
4graph2/complexity.go
-
2graph2/gqlgen.yml
-
6graph2/graph.go
-
157graph2/resolvers/character.go
-
14graph2/resolvers/resolvers.go
-
28graph2/types/log.go
-
3internal/auth/permitted.go
-
4models/changekeys/one.go
-
16models/changes/submit.go
-
17models/character.go
-
2models/tag.go
-
14models/tags/db.go
-
26models/tags/list.go
-
19repositories/character.go
-
1repositories/repository.go
-
2repositories/tag.go
-
207services/characters.go
-
224services/loaders/characterloader_gen.go
-
53services/loaders/loaders.go
-
12services/services.go
-
2services/tags.go
-
7tools.go
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="JavaScriptSettings"> |
||||
|
<option name="languageLevel" value="ES6" /> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="ProjectModuleManager"> |
||||
|
<modules> |
||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/rpdata-api.iml" filepath="$PROJECT_DIR$/.idea/rpdata-api.iml" /> |
||||
|
</modules> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="VcsDirectoryMappings"> |
||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" /> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="ProjectTasksOptions"> |
||||
|
<TaskOptions isEnabled="true"> |
||||
|
<option name="arguments" value="fmt $FilePath$" /> |
||||
|
<option name="checkSyntaxErrors" value="true" /> |
||||
|
<option name="description" /> |
||||
|
<option name="exitCodeBehavior" value="ERROR" /> |
||||
|
<option name="fileExtension" value="go" /> |
||||
|
<option name="immediateSync" value="false" /> |
||||
|
<option name="name" value="go fmt" /> |
||||
|
<option name="output" value="$FilePath$" /> |
||||
|
<option name="outputFilters"> |
||||
|
<array /> |
||||
|
</option> |
||||
|
<option name="outputFromStdout" value="false" /> |
||||
|
<option name="program" value="$GoExecPath$" /> |
||||
|
<option name="runOnExternalChanges" value="false" /> |
||||
|
<option name="scopeName" value="Project Files" /> |
||||
|
<option name="trackOnlyRoot" value="true" /> |
||||
|
<option name="workingDir" value="$ProjectFileDir$" /> |
||||
|
<envs> |
||||
|
<env name="GOROOT" value="$GOROOT$" /> |
||||
|
<env name="GOPATH" value="$GOPATH$" /> |
||||
|
<env name="PATH" value="$GoBinDirs$" /> |
||||
|
</envs> |
||||
|
</TaskOptions> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,586 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="ChangeListManager"> |
||||
|
<list default="true" id="ac85876f-1087-49cc-8c50-039d9d6340d7" name="Default Changelist" comment=""> |
||||
|
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" /> |
||||
|
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" /> |
||||
|
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" /> |
||||
|
<change afterPath="$PROJECT_DIR$/.idea/watcherTasks.xml" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/database/mongodb/db.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/mongodb/db.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/database/mongodb/tags.go" beforeDir="false" afterPath="$PROJECT_DIR$/database/mongodb/tags.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/complexity.go" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/complexity.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/gqlgen.yml" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/gqlgen.yml" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/graph.go" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/graph.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/resolvers/character.go" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/resolvers/character.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/resolvers/resolvers.go" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/resolvers/resolvers.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/graph2/types/log.go" beforeDir="false" afterPath="$PROJECT_DIR$/graph2/types/log.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/internal/auth/permitted.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/auth/permitted.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/changekeys/one.go" beforeDir="false" afterPath="$PROJECT_DIR$/models/changekeys/one.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/changes/submit.go" beforeDir="false" afterPath="$PROJECT_DIR$/models/changes/submit.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/character.go" beforeDir="false" afterPath="$PROJECT_DIR$/models/character.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/tag.go" beforeDir="false" afterPath="$PROJECT_DIR$/models/tag.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/tags/db.go" beforeDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/models/tags/list.go" beforeDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/repositories/repository.go" beforeDir="false" afterPath="$PROJECT_DIR$/repositories/repository.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/repositories/tag.go" beforeDir="false" afterPath="$PROJECT_DIR$/repositories/tag.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/services/services.go" beforeDir="false" afterPath="$PROJECT_DIR$/services/services.go" afterDir="false" /> |
||||
|
<change beforePath="$PROJECT_DIR$/services/tags.go" beforeDir="false" afterPath="$PROJECT_DIR$/services/tags.go" afterDir="false" /> |
||||
|
</list> |
||||
|
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> |
||||
|
<option name="SHOW_DIALOG" value="false" /> |
||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" /> |
||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> |
||||
|
<option name="LAST_RESOLUTION" value="IGNORE" /> |
||||
|
</component> |
||||
|
<component name="FileEditorManager"> |
||||
|
<leaf SIDE_TABS_SIZE_LIMIT_KEY="450"> |
||||
|
<file pinned="false" current-in-tab="true"> |
||||
|
<entry file="file://$PROJECT_DIR$/database/mongodb/characters.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="419"> |
||||
|
<caret line="109" column="4" selection-start-line="109" selection-start-column="4" selection-end-line="109" selection-end-column="4" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$USER_HOME$/go/pkg/mod/github.com/globalsign/mgo@v0.0.0-20180403085842-f76e4f9da92e/session.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="539"> |
||||
|
<caret line="2707" column="14" selection-start-line="2707" selection-start-column="5" selection-end-line="2707" selection-end-column="14" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$PROJECT_DIR$/services/characters.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1632"> |
||||
|
<caret line="63" column="25" lean-forward="true" selection-start-line="63" selection-start-column="25" selection-end-line="63" selection-end-column="25" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/listed.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="288"> |
||||
|
<caret line="6" column="14" selection-start-line="6" selection-start-column="14" selection-end-line="6" selection-end-column="14" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/many.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="816"> |
||||
|
<caret line="21" column="33" lean-forward="true" selection-start-line="21" selection-start-column="33" selection-end-line="21" selection-end-column="33" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/one.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="851"> |
||||
|
<caret line="25" lean-forward="true" selection-start-line="25" selection-end-line="25" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
<file pinned="false" current-in-tab="false"> |
||||
|
<entry file="file://$PROJECT_DIR$/repositories/character.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="576"> |
||||
|
<caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> |
||||
|
<folding> |
||||
|
<element signature="e#22#80#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
</leaf> |
||||
|
</component> |
||||
|
<component name="FileTemplateManagerImpl"> |
||||
|
<option name="RECENT_TEMPLATES"> |
||||
|
<list> |
||||
|
<option value="Go File" /> |
||||
|
</list> |
||||
|
</option> |
||||
|
</component> |
||||
|
<component name="FindInProjectRecents"> |
||||
|
<findStrings> |
||||
|
<find>delete(l.cache</find> |
||||
|
<find>dataloade</find> |
||||
|
</findStrings> |
||||
|
</component> |
||||
|
<component name="GOROOT" path="/usr/lib/go" /> |
||||
|
<component name="Git.Settings"> |
||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> |
||||
|
</component> |
||||
|
<component name="GoLibraries"> |
||||
|
<option name="indexEntireGoPath" value="false" /> |
||||
|
</component> |
||||
|
<component name="IdeDocumentHistory"> |
||||
|
<option name="CHANGED_PATHS"> |
||||
|
<list> |
||||
|
<option value="$PROJECT_DIR$/graph2/graphcore/exec_gen.go" /> |
||||
|
<option value="$PROJECT_DIR$/database/mongodb/tags.go" /> |
||||
|
<option value="$PROJECT_DIR$/repositories/tag.go" /> |
||||
|
<option value="$PROJECT_DIR$/services/services.go" /> |
||||
|
<option value="$PROJECT_DIR$/services/loaders/characterloader_gen.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/tools.go" /> |
||||
|
<option value="$PROJECT_DIR$/tools.go" /> |
||||
|
<option value="$PROJECT_DIR$/repositories/character.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/gqlgen.yml" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/resolvers/resolvers.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/resolvers/character.go" /> |
||||
|
<option value="$PROJECT_DIR$/database/mongodb/db.go" /> |
||||
|
<option value="$PROJECT_DIR$/services/tags.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/complexity.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/types/log.go" /> |
||||
|
<option value="$PROJECT_DIR$/graph2/graph.go" /> |
||||
|
<option value="$PROJECT_DIR$/services/loaders/loaders.go" /> |
||||
|
<option value="$PROJECT_DIR$/internal/auth/permitted.go" /> |
||||
|
<option value="$PROJECT_DIR$/models/changekeys/many.go" /> |
||||
|
<option value="$PROJECT_DIR$/models/changekeys/one.go" /> |
||||
|
<option value="$PROJECT_DIR$/models/token.go" /> |
||||
|
<option value="$PROJECT_DIR$/models/character.go" /> |
||||
|
<option value="$PROJECT_DIR$/services/characters.go" /> |
||||
|
<option value="$PROJECT_DIR$/database/mongodb/characters.go" /> |
||||
|
</list> |
||||
|
</option> |
||||
|
</component> |
||||
|
<component name="JsFlowSettings"> |
||||
|
<service-enabled>true</service-enabled> |
||||
|
<exe-path /> |
||||
|
<other-services-enabled>true</other-services-enabled> |
||||
|
<auto-save>true</auto-save> |
||||
|
</component> |
||||
|
<component name="ProjectFrameBounds" extendedState="6"> |
||||
|
<option name="x" value="1220" /> |
||||
|
<option name="y" value="600" /> |
||||
|
<option name="width" value="1400" /> |
||||
|
<option name="height" value="1000" /> |
||||
|
</component> |
||||
|
<component name="ProjectLevelVcsManager"> |
||||
|
<ConfirmationsSetting value="1" id="Add" /> |
||||
|
</component> |
||||
|
<component name="ProjectView"> |
||||
|
<navigator proportions="" version="1"> |
||||
|
<foldersAlwaysOnTop value="true" /> |
||||
|
</navigator> |
||||
|
<panes> |
||||
|
<pane id="Scope" /> |
||||
|
<pane id="ProjectPane"> |
||||
|
<subPane> |
||||
|
<expand> |
||||
|
<path> |
||||
|
<item name="rpdata-api" type="b2602c69:ProjectViewProjectNode" /> |
||||
|
<item name="rpdata-api" type="462c0819:PsiDirectoryNode" /> |
||||
|
</path> |
||||
|
<path> |
||||
|
<item name="rpdata-api" type="b2602c69:ProjectViewProjectNode" /> |
||||
|
<item name="rpdata-api" type="462c0819:PsiDirectoryNode" /> |
||||
|
<item name="database" type="462c0819:PsiDirectoryNode" /> |
||||
|
</path> |
||||
|
<path> |
||||
|
<item name="rpdata-api" type="b2602c69:ProjectViewProjectNode" /> |
||||
|
<item name="rpdata-api" type="462c0819:PsiDirectoryNode" /> |
||||
|
<item name="database" type="462c0819:PsiDirectoryNode" /> |
||||
|
<item name="mongodb" type="462c0819:PsiDirectoryNode" /> |
||||
|
</path> |
||||
|
<path> |
||||
|
<item name="rpdata-api" type="b2602c69:ProjectViewProjectNode" /> |
||||
|
<item name="rpdata-api" type="462c0819:PsiDirectoryNode" /> |
||||
|
<item name="services" type="462c0819:PsiDirectoryNode" /> |
||||
|
</path> |
||||
|
<path> |
||||
|
<item name="rpdata-api" type="b2602c69:ProjectViewProjectNode" /> |
||||
|
<item name="External Libraries" type="cb654da1:ExternalLibrariesNode" /> |
||||
|
</path> |
||||
|
</expand> |
||||
|
<select /> |
||||
|
</subPane> |
||||
|
</pane> |
||||
|
</panes> |
||||
|
</component> |
||||
|
<component name="PropertiesComponent"> |
||||
|
<property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" /> |
||||
|
<property name="DefaultGoTemplateProperty" value="Go File" /> |
||||
|
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" /> |
||||
|
<property name="WebServerToolWindowFactoryState" value="false" /> |
||||
|
<property name="configurable.Global.GOPATH.is.expanded" value="true" /> |
||||
|
<property name="configurable.Module.GOPATH.is.expanded" value="false" /> |
||||
|
<property name="configurable.Project.GOPATH.is.expanded" value="true" /> |
||||
|
<property name="go.gopath.indexing.explicitly.defined" value="true" /> |
||||
|
<property name="go.import.settings.migrated" value="true" /> |
||||
|
<property name="go.sdk.automatically.set" value="true" /> |
||||
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" /> |
||||
|
<property name="nodejs_package_manager_path" value="npm" /> |
||||
|
<property name="settings.editor.selected.configurable" value="reference.settingsdialog.IDE.editor.colors.Color Scheme Font" /> |
||||
|
</component> |
||||
|
<component name="RecentsManager"> |
||||
|
<key name="MoveFile.RECENT_KEYS"> |
||||
|
<recent name="$PROJECT_DIR$" /> |
||||
|
</key> |
||||
|
</component> |
||||
|
<component name="RunDashboard"> |
||||
|
<option name="ruleStates"> |
||||
|
<list> |
||||
|
<RuleState> |
||||
|
<option name="name" value="ConfigurationTypeDashboardGroupingRule" /> |
||||
|
</RuleState> |
||||
|
<RuleState> |
||||
|
<option name="name" value="StatusDashboardGroupingRule" /> |
||||
|
</RuleState> |
||||
|
</list> |
||||
|
</option> |
||||
|
</component> |
||||
|
<component name="TodoView"> |
||||
|
<todo-panel id="selected-file"> |
||||
|
<is-autoscroll-to-source value="true" /> |
||||
|
</todo-panel> |
||||
|
<todo-panel id="all"> |
||||
|
<are-packages-shown value="true" /> |
||||
|
<is-autoscroll-to-source value="true" /> |
||||
|
</todo-panel> |
||||
|
</component> |
||||
|
<component name="ToolWindowManager"> |
||||
|
<frame x="0" y="41" width="3840" height="2119" extended-state="6" /> |
||||
|
<editor active="true" /> |
||||
|
<layout> |
||||
|
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.5998948" visible="true" weight="0.109684326" /> |
||||
|
<window_info active="true" id="Structure" order="1" sideWeight="0.40010515" side_tool="true" visible="true" weight="0.109684326" /> |
||||
|
<window_info id="Favorites" order="2" side_tool="true" /> |
||||
|
<window_info anchor="bottom" id="GraphQL" /> |
||||
|
<window_info anchor="bottom" id="Message" order="0" /> |
||||
|
<window_info anchor="bottom" id="Find" order="1" /> |
||||
|
<window_info anchor="bottom" id="Run" order="2" weight="0.32993016" /> |
||||
|
<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" weight="0.32993016" /> |
||||
|
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" /> |
||||
|
<window_info anchor="bottom" id="Database Changes" order="8" /> |
||||
|
<window_info anchor="bottom" id="Version Control" order="9" /> |
||||
|
<window_info anchor="bottom" id="Terminal" order="10" /> |
||||
|
<window_info anchor="bottom" id="Event Log" order="11" side_tool="true" weight="0.32993016" /> |
||||
|
<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="Database" order="3" /> |
||||
|
</layout> |
||||
|
</component> |
||||
|
<component name="TypeScriptGeneratedFilesManager"> |
||||
|
<option name="version" value="1" /> |
||||
|
</component> |
||||
|
<component name="VgoProject"> |
||||
|
<integration-enabled>true</integration-enabled> |
||||
|
</component> |
||||
|
<component name="editorHistoryManager"> |
||||
|
<entry file="file://$PROJECT_DIR$/internal/store/space.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor" /> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/database/mongodb/tags.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1200"> |
||||
|
<caret line="34" column="27" selection-start-line="34" selection-start-column="27" selection-end-line="34" selection-end-column="27" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/repositories/tag.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="288"> |
||||
|
<caret line="10" column="49" selection-start-line="10" selection-start-column="42" selection-end-line="10" selection-end-column="49" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/.vscode/settings.json"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="144"> |
||||
|
<caret line="3" selection-start-line="3" selection-end-line="3" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/characters/list.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="-1095" /> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$USER_HOME$/go/pkg/mod/github.com/globalsign/mgo@v0.0.0-20180403085842-f76e4f9da92e/bulk.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1608"> |
||||
|
<caret line="353" column="22" selection-start-line="353" selection-start-column="22" selection-end-line="353" selection-end-column="22" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/internal/store/db.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="-972" /> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/logs/find.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor" /> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/logs/list.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="-432" /> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/logs/add.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="336"> |
||||
|
<caret line="7" column="22" selection-start-line="7" selection-start-column="22" selection-end-line="7" selection-end-column="22" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/internal/counter/counter.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1305"> |
||||
|
<caret line="52" column="27" selection-start-line="52" selection-start-column="27" selection-end-line="52" selection-end-column="40" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/posts/remove.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1056"> |
||||
|
<caret line="26" column="1" lean-forward="true" selection-start-line="26" selection-start-column="1" selection-end-line="26" selection-end-column="1" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/repositories/repository.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor" /> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/characters/db.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="441"> |
||||
|
<caret line="34" selection-start-line="34" selection-end-line="52" selection-end-column="9" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/graphcore/input_gen.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="288"> |
||||
|
<caret line="6" selection-start-line="6" selection-end-line="6" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/schema/types/Character.gql"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="192"> |
||||
|
<caret line="4" lean-forward="true" selection-start-line="4" selection-end-line="4" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/tools.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="336"> |
||||
|
<caret line="7" selection-start-line="7" selection-end-line="7" /> |
||||
|
<folding> |
||||
|
<element signature="e#30#74#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/go.mod"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="480"> |
||||
|
<caret line="10" column="22" selection-start-line="10" selection-start-column="22" selection-end-line="10" selection-end-column="22" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/database/database.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor" /> |
||||
|
</entry> |
||||
|
<entry file="file://$USER_HOME$/go/pkg/mod/github.com/globalsign/mgo@v0.0.0-20180403085842-f76e4f9da92e/bson/bson.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="582"> |
||||
|
<caret line="154" column="5" selection-start-line="154" selection-start-column="5" selection-end-line="154" selection-end-column="6" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/.drone.yml"> |
||||
|
<provider selected="true" editor-type-id="text-editor" /> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/gqlgen.yml"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="755"> |
||||
|
<caret line="23" column="10" lean-forward="true" selection-start-line="23" selection-start-column="10" selection-end-line="23" selection-end-column="10" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/resolvers/resolvers.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="336"> |
||||
|
<caret line="10" lean-forward="true" selection-start-line="10" selection-end-line="10" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/database/mongodb/db.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="803"> |
||||
|
<caret line="81" column="30" selection-start-line="81" selection-start-column="30" selection-end-line="81" selection-end-column="30" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/services/tags.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="720"> |
||||
|
<caret line="15" column="71" selection-start-line="15" selection-start-column="71" selection-end-line="15" selection-end-column="71" /> |
||||
|
<folding> |
||||
|
<element signature="e#18#118#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/complexity.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="624"> |
||||
|
<caret line="13" column="13" lean-forward="true" selection-start-line="13" selection-start-column="13" selection-end-line="13" selection-end-column="13" /> |
||||
|
<folding> |
||||
|
<element signature="e#16#377#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/services/services.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="528"> |
||||
|
<caret line="11" column="1" lean-forward="true" selection-start-line="11" selection-start-column="1" selection-end-line="11" selection-end-column="1" /> |
||||
|
<folding> |
||||
|
<element signature="e#18#116#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/graphcore/exec_gen.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="587"> |
||||
|
<caret line="1596" selection-start-line="1596" selection-end-line="1596" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/graph.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1283"> |
||||
|
<caret line="37" column="11" lean-forward="true" selection-start-line="37" selection-start-column="11" selection-end-line="37" selection-end-column="11" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/types/log.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="515"> |
||||
|
<caret line="30" column="26" selection-start-line="30" selection-start-column="26" selection-end-line="30" selection-end-column="26" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/services/loaders/characterloader_gen.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="587"> |
||||
|
<caret line="115" column="26" selection-start-line="115" selection-start-column="26" selection-end-line="115" selection-end-column="26" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/graph2/resolvers/character.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="899"> |
||||
|
<caret line="29" column="12" lean-forward="true" selection-start-line="29" selection-start-column="12" selection-end-line="29" selection-end-column="12" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/services/loaders/loaders.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="288"> |
||||
|
<caret line="6" selection-start-line="6" selection-end-line="6" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/many.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="816"> |
||||
|
<caret line="21" column="33" lean-forward="true" selection-start-line="21" selection-start-column="33" selection-end-line="21" selection-end-column="33" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/listed.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="288"> |
||||
|
<caret line="6" column="14" selection-start-line="6" selection-start-column="14" selection-end-line="6" selection-end-column="14" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changes/submit.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="723"> |
||||
|
<caret line="72" column="25" lean-forward="true" selection-start-line="72" selection-start-column="25" selection-end-line="72" selection-end-column="25" /> |
||||
|
<folding> |
||||
|
<element signature="e#17#136#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$USER_HOME$/go/pkg/mod/github.com/globalsign/mgo@v0.0.0-20180403085842-f76e4f9da92e/session.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="539"> |
||||
|
<caret line="2707" column="14" selection-start-line="2707" selection-start-column="5" selection-end-line="2707" selection-end-column="14" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/token.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1235"> |
||||
|
<caret line="41" selection-start-line="41" selection-end-line="41" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/internal/auth/permitted.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="816"> |
||||
|
<caret line="29" column="32" lean-forward="true" selection-start-line="29" selection-start-column="32" selection-end-line="29" selection-end-column="32" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/character.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1043"> |
||||
|
<caret line="45" column="14" selection-start-line="45" selection-start-column="14" selection-end-line="45" selection-end-column="14" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/models/changekeys/one.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="851"> |
||||
|
<caret line="25" lean-forward="true" selection-start-line="25" selection-end-line="25" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/services/characters.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="1632"> |
||||
|
<caret line="63" column="25" lean-forward="true" selection-start-line="63" selection-start-column="25" selection-end-line="63" selection-end-column="25" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/repositories/character.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="576"> |
||||
|
<caret line="12" column="4" selection-start-line="12" selection-start-column="4" selection-end-line="12" selection-end-column="4" /> |
||||
|
<folding> |
||||
|
<element signature="e#22#80#0" expanded="true" /> |
||||
|
</folding> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
<entry file="file://$PROJECT_DIR$/database/mongodb/characters.go"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="419"> |
||||
|
<caret line="109" column="4" selection-start-line="109" selection-start-column="4" selection-end-line="109" selection-end-column="4" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,217 @@ |
|||||
|
package mongodb |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
"github.com/globalsign/mgo" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
"sort" |
||||
|
"strconv" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/repositories" |
||||
|
) |
||||
|
|
||||
|
type characterRepository struct { |
||||
|
characters *mgo.Collection |
||||
|
cidCounter *counter |
||||
|
} |
||||
|
|
||||
|
func newCharacterRepository(db *mgo.Database) (repositories.CharacterRepository, error) { |
||||
|
collection := db.C("common.characters") |
||||
|
|
||||
|
err := collection.EnsureIndexKey("name") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = collection.EnsureIndexKey("shortName") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = collection.EnsureIndexKey("author") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"nicks"}, |
||||
|
Unique: true, |
||||
|
DropDups: true, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"$text:description"}, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &characterRepository{ |
||||
|
characters: collection, |
||||
|
cidCounter: newCounter(db, "auto_increment", "Character"), |
||||
|
}, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) Find(ctx context.Context, id string) (*models.Character, error) { |
||||
|
character := new(models.Character) |
||||
|
err := r.characters.FindId(id).One(character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) FindNick(ctx context.Context, nick string) (*models.Character, error) { |
||||
|
character := new(models.Character) |
||||
|
err := r.characters.Find(bson.M{"nick": nick}).One(character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) { |
||||
|
query := bson.M{} |
||||
|
if filter.Author != nil { |
||||
|
query["author"] = *filter.Author |
||||
|
} |
||||
|
if len(filter.IDs) > 0 { |
||||
|
query["_id"] = bson.M{"$in": filter.IDs} |
||||
|
} |
||||
|
if len(filter.Nicks) > 0 { |
||||
|
query["nicks"] = bson.M{"$in": filter.Nicks} |
||||
|
} |
||||
|
if len(filter.Names) > 0 { |
||||
|
query["$or"] = []bson.M{ |
||||
|
{"name": bson.M{"$in": filter.Names}}, |
||||
|
{"shortName": bson.M{"$in": filter.Names}}, |
||||
|
} |
||||
|
} |
||||
|
if filter.Search != nil { |
||||
|
query["$text"] = bson.M{"$search": *filter.Search} |
||||
|
} |
||||
|
|
||||
|
characters := make([]*models.Character, 0, 32) |
||||
|
err := r.characters.Find(query).All(&characters) |
||||
|
if err != nil { |
||||
|
if err == mgo.ErrNotFound { |
||||
|
return characters, nil |
||||
|
} |
||||
|
|
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
sort.Slice(characters, func(i, j int) bool { |
||||
|
ni, _ := strconv.Atoi(characters[i].ID[1:]) |
||||
|
nj, _ := strconv.Atoi(characters[j].ID[1:]) |
||||
|
|
||||
|
return ni < nj |
||||
|
}) |
||||
|
|
||||
|
return characters, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) { |
||||
|
nextId, err := r.cidCounter.Increment(1) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
character.ID = "C" + strconv.Itoa(nextId) |
||||
|
|
||||
|
err = r.characters.Insert(&character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) { |
||||
|
updateBson := bson.M{} |
||||
|
if update.Name != nil { |
||||
|
updateBson["name"] = *update.Name |
||||
|
character.Name = *update.Name |
||||
|
} |
||||
|
if update.ShortName != nil { |
||||
|
updateBson["shortName"] = *update.ShortName |
||||
|
character.ShortName = *update.ShortName |
||||
|
} |
||||
|
if update.Description != nil { |
||||
|
updateBson["description"] = *update.Description |
||||
|
character.Description = *update.Description |
||||
|
} |
||||
|
|
||||
|
err := r.characters.UpdateId(character.ID, bson.M{"$set": updateBson}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return &character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) { |
||||
|
if character.HasNick(nick) { |
||||
|
return nil, errors.New("nick already exist") |
||||
|
} |
||||
|
|
||||
|
match := bson.M{ |
||||
|
"_id": character.ID, |
||||
|
"nicks": bson.M{"$ne": nick}, |
||||
|
} |
||||
|
|
||||
|
err := r.characters.Update(match, bson.M{"$push": bson.M{"nicks": nick}}) |
||||
|
if err == mgo.ErrNotFound { |
||||
|
return nil, repositories.ErrNotFound |
||||
|
} else if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1) |
||||
|
copy(newNicks, character.Nicks) |
||||
|
character.Nicks = append(newNicks, nick) |
||||
|
|
||||
|
return &character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) { |
||||
|
if !character.HasNick(nick) { |
||||
|
return nil, errors.New("nick does not exist") |
||||
|
} |
||||
|
|
||||
|
match := bson.M{ |
||||
|
"_id": character.ID, |
||||
|
"nicks": nick, |
||||
|
} |
||||
|
|
||||
|
err := r.characters.Update(match, bson.M{"$pull": bson.M{"nicks": nick}}) |
||||
|
if err == mgo.ErrNotFound { |
||||
|
return nil, repositories.ErrNotFound |
||||
|
} else if mErr, ok := err.(*mgo.LastError); ok && mErr.Code == 11000 { |
||||
|
return nil, errors.New("The nick belongs to another character already") |
||||
|
} else if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
newNicks := make([]string, len(character.Nicks), len(character.Nicks)+1) |
||||
|
copy(newNicks, character.Nicks) |
||||
|
for i := range newNicks { |
||||
|
if newNicks[i] == nick { |
||||
|
newNicks = append(newNicks[:i], newNicks[i+1:]...) |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
character.Nicks = newNicks |
||||
|
|
||||
|
return &character, nil |
||||
|
} |
||||
|
|
||||
|
func (r *characterRepository) Delete(ctx context.Context, character models.Character) error { |
||||
|
return r.characters.RemoveId(character.ID) |
||||
|
} |
@ -1,14 +0,0 @@ |
|||||
package tags |
|
||||
|
|
||||
import ( |
|
||||
"git.aiterp.net/rpdata/api/internal/store" |
|
||||
"github.com/globalsign/mgo" |
|
||||
) |
|
||||
|
|
||||
var storyCollection *mgo.Collection |
|
||||
|
|
||||
func init() { |
|
||||
store.HandleInit(func(db *mgo.Database) { |
|
||||
storyCollection = db.C("story.stories") |
|
||||
}) |
|
||||
} |
|
@ -1,26 +0,0 @@ |
|||||
package tags |
|
||||
|
|
||||
import ( |
|
||||
"sort" |
|
||||
"strings" |
|
||||
|
|
||||
"git.aiterp.net/rpdata/api/models" |
|
||||
"github.com/globalsign/mgo/bson" |
|
||||
) |
|
||||
|
|
||||
// List lists all tags
|
|
||||
func List() ([]models.Tag, error) { |
|
||||
tags := make([]models.Tag, 0, 64) |
|
||||
err := storyCollection.Find(bson.M{"listed": true, "tags": bson.M{"$ne": nil}}).Distinct("tags", &tags) |
|
||||
|
|
||||
sort.Slice(tags, func(i, j int) bool { |
|
||||
kindCmp := strings.Compare(string(tags[i].Kind), string(tags[j].Kind)) |
|
||||
if kindCmp != 0 { |
|
||||
return kindCmp < 0 |
|
||||
} |
|
||||
|
|
||||
return strings.Compare(tags[i].Name, tags[j].Name) < 0 |
|
||||
}) |
|
||||
|
|
||||
return tags, err |
|
||||
} |
|
@ -0,0 +1,19 @@ |
|||||
|
package repositories |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
// CharacterRepository is an interface for a database using logs.
|
||||
|
type CharacterRepository interface { |
||||
|
Find(ctx context.Context, id string) (*models.Character, error) |
||||
|
FindNick(ctx context.Context, nick string) (*models.Character, error) |
||||
|
List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) |
||||
|
Insert(ctx context.Context, character models.Character) (*models.Character, error) |
||||
|
Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) |
||||
|
AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) |
||||
|
RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) |
||||
|
Delete(ctx context.Context, character models.Character) error |
||||
|
} |
@ -0,0 +1,207 @@ |
|||||
|
package services |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
"git.aiterp.net/rpdata/api/internal/auth" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/models/changekeys" |
||||
|
"git.aiterp.net/rpdata/api/models/changes" |
||||
|
"git.aiterp.net/rpdata/api/repositories" |
||||
|
"git.aiterp.net/rpdata/api/services/loaders" |
||||
|
"sort" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
type CharacterService struct { |
||||
|
characters repositories.CharacterRepository |
||||
|
loader *loaders.CharacterLoader |
||||
|
} |
||||
|
|
||||
|
// Find uses the loader to find the character by the ID.
|
||||
|
func (s *CharacterService) Find(ctx context.Context, id string) (*models.Character, error) { |
||||
|
return s.loader.Load(id) |
||||
|
} |
||||
|
|
||||
|
// Find uses the loader to find the character by the ID.
|
||||
|
func (s *CharacterService) FindNick(ctx context.Context, nick string) (*models.Character, error) { |
||||
|
return s.characters.FindNick(ctx, nick) |
||||
|
} |
||||
|
|
||||
|
// List lists the characters. If the only filter active is `IDs`, the loader is used to batch together requests.
|
||||
|
func (s *CharacterService) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) { |
||||
|
if len(filter.IDs) > 0 && len(filter.Names) == 0 && len(filter.Nicks) == 0 && filter.Author == nil && filter.Search == nil { |
||||
|
characters, errs := s.loader.LoadAll(filter.IDs) |
||||
|
if len(characters) == 0 && len(errs) > 0 { |
||||
|
if errs[0] == repositories.ErrNotFound { |
||||
|
return []*models.Character{}, nil |
||||
|
} else { |
||||
|
return nil, errs[0] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if err := ctx.Err(); err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
var badIndices []int |
||||
|
for i, character := range characters { |
||||
|
if character == nil { |
||||
|
badIndices = append(badIndices, i-len(badIndices)) |
||||
|
} |
||||
|
} |
||||
|
for _, index := range badIndices { |
||||
|
characters = append(characters[:index], characters[index+1:]...) |
||||
|
} |
||||
|
|
||||
|
sort.Slice(characters, func(i, j int) bool { |
||||
|
return strings.Compare(characters[i].ID, characters[j].ID) < 0 |
||||
|
}) |
||||
|
|
||||
|
return characters, nil |
||||
|
} |
||||
|
|
||||
|
return s.characters.List(ctx, filter) |
||||
|
} |
||||
|
|
||||
|
func (s *CharacterService) Create(ctx context.Context, nick, name, shortName, author, description string) (*models.Character, error) { |
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
if token == nil { |
||||
|
return nil, auth.ErrUnauthenticated |
||||
|
} |
||||
|
|
||||
|
if name == "" { |
||||
|
return nil, errors.New("Name cannot be empty") |
||||
|
} |
||||
|
|
||||
|
if author == "" { |
||||
|
author = token.UserID |
||||
|
} |
||||
|
|
||||
|
if shortName == "" { |
||||
|
split := strings.SplitN(name, " ", 2) |
||||
|
shortName = split[0] |
||||
|
} |
||||
|
|
||||
|
character := &models.Character{ |
||||
|
Name: name, |
||||
|
ShortName: shortName, |
||||
|
Author: author, |
||||
|
Nicks: []string{nick}, |
||||
|
Description: description, |
||||
|
} |
||||
|
|
||||
|
err := auth.CheckPermission(ctx, "add", character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
character, err = s.characters.Insert(ctx, *character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
//TODO: New change submit system
|
||||
|
go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character) |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (s *CharacterService) Update(ctx context.Context, id string, name, shortName, description *string) (*models.Character, error) { |
||||
|
character, err := s.characters.Find(ctx, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = auth.CheckPermission(ctx, "edit", character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
character, err = s.characters.Update(ctx, *character, models.CharacterUpdate{ |
||||
|
Name: name, |
||||
|
ShortName: shortName, |
||||
|
Description: description, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
s.loader.Clear(character.ID) |
||||
|
s.loader.Prime(character.ID, character) |
||||
|
|
||||
|
//TODO: New change submit system
|
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (s *CharacterService) AddNick(ctx context.Context, id string, nick string) (*models.Character, error) { |
||||
|
character, err := s.characters.Find(ctx, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = auth.CheckPermission(ctx, "edit", character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
character, err = s.characters.AddNick(ctx, *character, nick) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
//TODO: New change submit system
|
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (s *CharacterService) RemoveNick(ctx context.Context, id string, nick string) (*models.Character, error) { |
||||
|
character, err := s.characters.Find(ctx, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = auth.CheckPermission(ctx, "edit", character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
character, err = s.characters.RemoveNick(ctx, *character, nick) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
//TODO: New change submit system
|
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func (s *CharacterService) Delete(ctx context.Context, id string) (*models.Character, error) { |
||||
|
character, err := s.characters.Find(ctx, id) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = auth.CheckPermission(ctx, "edit", character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
err = s.characters.Delete(ctx, *character) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
//TODO: New change submit system
|
||||
|
token := auth.TokenFromContext(ctx) |
||||
|
go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character) |
||||
|
|
||||
|
return character, nil |
||||
|
} |
@ -0,0 +1,224 @@ |
|||||
|
// Code generated by github.com/vektah/dataloaden, DO NOT EDIT.
|
||||
|
|
||||
|
package loaders |
||||
|
|
||||
|
import ( |
||||
|
"sync" |
||||
|
"time" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
// CharacterLoaderConfig captures the config to create a new CharacterLoader
|
||||
|
type CharacterLoaderConfig struct { |
||||
|
// Fetch is a method that provides the data for the loader
|
||||
|
Fetch func(keys []string) ([]*models.Character, []error) |
||||
|
|
||||
|
// Wait is how long wait before sending a batch
|
||||
|
Wait time.Duration |
||||
|
|
||||
|
// MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit
|
||||
|
MaxBatch int |
||||
|
} |
||||
|
|
||||
|
// NewCharacterLoader creates a new CharacterLoader given a fetch, wait, and maxBatch
|
||||
|
func NewCharacterLoader(config CharacterLoaderConfig) *CharacterLoader { |
||||
|
return &CharacterLoader{ |
||||
|
fetch: config.Fetch, |
||||
|
wait: config.Wait, |
||||
|
maxBatch: config.MaxBatch, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// CharacterLoader batches and caches requests
|
||||
|
type CharacterLoader struct { |
||||
|
// this method provides the data for the loader
|
||||
|
fetch func(keys []string) ([]*models.Character, []error) |
||||
|
|
||||
|
// how long to done before sending a batch
|
||||
|
wait time.Duration |
||||
|
|
||||
|
// this will limit the maximum number of keys to send in one batch, 0 = no limit
|
||||
|
maxBatch int |
||||
|
|
||||
|
// INTERNAL
|
||||
|
|
||||
|
// lazily created cache
|
||||
|
cache map[string]*models.Character |
||||
|
|
||||
|
// the current batch. keys will continue to be collected until timeout is hit,
|
||||
|
// then everything will be sent to the fetch method and out to the listeners
|
||||
|
batch *characterLoaderBatch |
||||
|
|
||||
|
// mutex to prevent races
|
||||
|
mu sync.Mutex |
||||
|
} |
||||
|
|
||||
|
type characterLoaderBatch struct { |
||||
|
keys []string |
||||
|
data []*models.Character |
||||
|
error []error |
||||
|
closing bool |
||||
|
done chan struct{} |
||||
|
} |
||||
|
|
||||
|
// Load a Character by key, batching and caching will be applied automatically
|
||||
|
func (l *CharacterLoader) Load(key string) (*models.Character, error) { |
||||
|
return l.LoadThunk(key)() |
||||
|
} |
||||
|
|
||||
|
// LoadThunk returns a function that when called will block waiting for a Character.
|
||||
|
// This method should be used if you want one goroutine to make requests to many
|
||||
|
// different data loaders without blocking until the thunk is called.
|
||||
|
func (l *CharacterLoader) LoadThunk(key string) func() (*models.Character, error) { |
||||
|
l.mu.Lock() |
||||
|
if it, ok := l.cache[key]; ok { |
||||
|
l.mu.Unlock() |
||||
|
return func() (*models.Character, error) { |
||||
|
return it, nil |
||||
|
} |
||||
|
} |
||||
|
if l.batch == nil { |
||||
|
l.batch = &characterLoaderBatch{done: make(chan struct{})} |
||||
|
} |
||||
|
batch := l.batch |
||||
|
pos := batch.keyIndex(l, key) |
||||
|
l.mu.Unlock() |
||||
|
|
||||
|
return func() (*models.Character, error) { |
||||
|
<-batch.done |
||||
|
|
||||
|
var data *models.Character |
||||
|
if pos < len(batch.data) { |
||||
|
data = batch.data[pos] |
||||
|
} |
||||
|
|
||||
|
var err error |
||||
|
// its convenient to be able to return a single error for everything
|
||||
|
if len(batch.error) == 1 { |
||||
|
err = batch.error[0] |
||||
|
} else if batch.error != nil { |
||||
|
err = batch.error[pos] |
||||
|
} |
||||
|
|
||||
|
if err == nil { |
||||
|
l.mu.Lock() |
||||
|
l.unsafeSet(key, data) |
||||
|
l.mu.Unlock() |
||||
|
} |
||||
|
|
||||
|
return data, err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// LoadAll fetches many keys at once. It will be broken into appropriate sized
|
||||
|
// sub batches depending on how the loader is configured
|
||||
|
func (l *CharacterLoader) LoadAll(keys []string) ([]*models.Character, []error) { |
||||
|
results := make([]func() (*models.Character, error), len(keys)) |
||||
|
|
||||
|
for i, key := range keys { |
||||
|
results[i] = l.LoadThunk(key) |
||||
|
} |
||||
|
|
||||
|
characters := make([]*models.Character, len(keys)) |
||||
|
errors := make([]error, len(keys)) |
||||
|
for i, thunk := range results { |
||||
|
characters[i], errors[i] = thunk() |
||||
|
} |
||||
|
return characters, errors |
||||
|
} |
||||
|
|
||||
|
// LoadAllThunk returns a function that when called will block waiting for a Characters.
|
||||
|
// This method should be used if you want one goroutine to make requests to many
|
||||
|
// different data loaders without blocking until the thunk is called.
|
||||
|
func (l *CharacterLoader) LoadAllThunk(keys []string) func() ([]*models.Character, []error) { |
||||
|
results := make([]func() (*models.Character, error), len(keys)) |
||||
|
for i, key := range keys { |
||||
|
results[i] = l.LoadThunk(key) |
||||
|
} |
||||
|
return func() ([]*models.Character, []error) { |
||||
|
characters := make([]*models.Character, len(keys)) |
||||
|
errors := make([]error, len(keys)) |
||||
|
for i, thunk := range results { |
||||
|
characters[i], errors[i] = thunk() |
||||
|
} |
||||
|
return characters, errors |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Prime the cache with the provided key and value. If the key already exists, no change is made
|
||||
|
// and false is returned.
|
||||
|
// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).)
|
||||
|
func (l *CharacterLoader) Prime(key string, value *models.Character) bool { |
||||
|
l.mu.Lock() |
||||
|
var found bool |
||||
|
if _, found = l.cache[key]; !found { |
||||
|
// make a copy when writing to the cache, its easy to pass a pointer in from a loop var
|
||||
|
// and end up with the whole cache pointing to the same value.
|
||||
|
cpy := *value |
||||
|
l.unsafeSet(key, &cpy) |
||||
|
} |
||||
|
l.mu.Unlock() |
||||
|
return !found |
||||
|
} |
||||
|
|
||||
|
// Clear the value at key from the cache, if it exists
|
||||
|
func (l *CharacterLoader) Clear(key string) { |
||||
|
l.mu.Lock() |
||||
|
delete(l.cache, key) |
||||
|
l.mu.Unlock() |
||||
|
} |
||||
|
|
||||
|
func (l *CharacterLoader) unsafeSet(key string, value *models.Character) { |
||||
|
if l.cache == nil { |
||||
|
l.cache = map[string]*models.Character{} |
||||
|
} |
||||
|
l.cache[key] = value |
||||
|
} |
||||
|
|
||||
|
// keyIndex will return the location of the key in the batch, if its not found
|
||||
|
// it will add the key to the batch
|
||||
|
func (b *characterLoaderBatch) keyIndex(l *CharacterLoader, key string) int { |
||||
|
for i, existingKey := range b.keys { |
||||
|
if key == existingKey { |
||||
|
return i |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pos := len(b.keys) |
||||
|
b.keys = append(b.keys, key) |
||||
|
if pos == 0 { |
||||
|
go b.startTimer(l) |
||||
|
} |
||||
|
|
||||
|
if l.maxBatch != 0 && pos >= l.maxBatch-1 { |
||||
|
if !b.closing { |
||||
|
b.closing = true |
||||
|
l.batch = nil |
||||
|
go b.end(l) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return pos |
||||
|
} |
||||
|
|
||||
|
func (b *characterLoaderBatch) startTimer(l *CharacterLoader) { |
||||
|
time.Sleep(l.wait) |
||||
|
l.mu.Lock() |
||||
|
|
||||
|
// we must have hit a batch limit and are already finalizing this batch
|
||||
|
if b.closing { |
||||
|
l.mu.Unlock() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
l.batch = nil |
||||
|
l.mu.Unlock() |
||||
|
|
||||
|
b.end(l) |
||||
|
} |
||||
|
|
||||
|
func (b *characterLoaderBatch) end(l *CharacterLoader) { |
||||
|
b.data, b.error = l.fetch(b.keys) |
||||
|
close(b.done) |
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
package loaders |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/repositories" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
//go:generate go run github.com/vektah/dataloaden CharacterLoader string *git.aiterp.net/rpdata/api/models.Character
|
||||
|
|
||||
|
// CharacterLoaderFromRepository creates a new CharacterLoader
|
||||
|
func CharacterLoaderFromRepository(repo repositories.CharacterRepository) *CharacterLoader { |
||||
|
return &CharacterLoader{ |
||||
|
wait: time.Millisecond * 1, |
||||
|
maxBatch: 100, |
||||
|
fetch: func(keys []string) ([]*models.Character, []error) { |
||||
|
timeout, cancel := context.WithTimeout(context.Background(), time.Second*15) |
||||
|
defer cancel() |
||||
|
|
||||
|
characters, err := repo.List(timeout, models.CharacterFilter{ |
||||
|
IDs: keys, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
errs := make([]error, len(keys)) |
||||
|
for i := range errs { |
||||
|
errs[i] = err |
||||
|
} |
||||
|
|
||||
|
return nil, errs |
||||
|
} |
||||
|
|
||||
|
charMap := make(map[string]*models.Character, len(keys)) |
||||
|
|
||||
|
for _, character := range characters { |
||||
|
charMap[character.ID] = character |
||||
|
} |
||||
|
|
||||
|
results := make([]*models.Character, len(keys)) |
||||
|
errs := make([]error, len(keys)) |
||||
|
|
||||
|
for i, key := range keys { |
||||
|
if character, ok := charMap[key]; ok { |
||||
|
results[i] = character |
||||
|
} else { |
||||
|
errs[i] = repositories.ErrNotFound |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return results, errs |
||||
|
}, |
||||
|
} |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
//+build tools
|
||||
|
|
||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
_ "github.com/vektah/dataloaden" |
||||
|
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue