Browse Source

Added character service and such.

thegreatrefactor
Gisle Aune 5 years ago
parent
commit
2a1423d28f
  1. 6
      .idea/misc.xml
  2. 8
      .idea/modules.xml
  3. 6
      .idea/vcs.xml
  4. 29
      .idea/watcherTasks.xml
  5. 586
      .idea/workspace.xml
  6. 217
      database/mongodb/characters.go
  7. 53
      database/mongodb/db.go
  8. 10
      database/mongodb/tags.go
  9. 30
      go.mod
  10. 57
      go.sum
  11. 4
      graph2/complexity.go
  12. 2
      graph2/gqlgen.yml
  13. 6
      graph2/graph.go
  14. 157
      graph2/resolvers/character.go
  15. 14
      graph2/resolvers/resolvers.go
  16. 28
      graph2/types/log.go
  17. 3
      internal/auth/permitted.go
  18. 4
      models/changekeys/one.go
  19. 16
      models/changes/submit.go
  20. 19
      models/character.go
  21. 2
      models/tag.go
  22. 14
      models/tags/db.go
  23. 26
      models/tags/list.go
  24. 19
      repositories/character.go
  25. 3
      repositories/repository.go
  26. 2
      repositories/tag.go
  27. 207
      services/characters.go
  28. 224
      services/loaders/characterloader_gen.go
  29. 53
      services/loaders/loaders.go
  30. 14
      services/services.go
  31. 2
      services/tags.go
  32. 7
      tools.go

6
.idea/misc.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml

@ -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>

6
.idea/vcs.xml

@ -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>

29
.idea/watcherTasks.xml

@ -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>

586
.idea/workspace.xml

@ -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>

217
database/mongodb/characters.go

@ -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)
}

53
database/mongodb/db.go

@ -2,6 +2,7 @@ package mongodb
import (
"fmt"
"github.com/globalsign/mgo/bson"
"time"
"git.aiterp.net/rpdata/api/internal/config"
@ -31,8 +32,15 @@ func Init(cfg config.Database) (bundle *repositories.Bundle, closeFn func() erro
db := session.DB(cfg.Db)
characters, err := newCharacterRepository(db)
if err != nil {
session.Close()
return nil, nil, err
}
bundle = &repositories.Bundle{
Tags: newTagRepository(db),
Characters: characters,
Tags: newTagRepository(db),
}
closeFn = func() error {
@ -42,3 +50,46 @@ func Init(cfg config.Database) (bundle *repositories.Bundle, closeFn func() erro
return
}
type counter struct {
coll *mgo.Collection
category string
name string
}
func newCounter(db *mgo.Database, category, name string) *counter {
return &counter{
coll: db.C("core.counters"),
category: category,
name: name,
}
}
func (c *counter) WithName(name string) *counter {
return &counter{
coll: c.coll,
category: c.category,
name: name,
}
}
func (c *counter) Increment(amount int) (int, error) {
type counterDoc struct {
ID string `bson:"_id"`
Value int `bson:"value"`
}
id := c.category + "." + c.name
doc := counterDoc{}
_, err := c.coll.Find(bson.M{"_id": id}).Apply(mgo.Change{
Update: bson.M{"$inc": bson.M{"value": amount}},
Upsert: true,
ReturnNew: true,
}, &doc)
if err != nil {
return -1, err
}
return doc.Value, nil
}

10
database/mongodb/tags.go

@ -21,7 +21,7 @@ func newTagRepository(db *mgo.Database) repositories.TagRepository {
}
}
func (r *tagRepository) Find(ctx context.Context, kind, name string) (*models.Tag, error) {
func (r *tagRepository) Find(ctx context.Context, kind models.TagKind, name string) (*models.Tag, error) {
tags := make([]*models.Tag, 0, 1)
err := r.stories.Find(bson.M{"listed": true, "tags": bson.M{"kind": kind, "name": name}}).Distinct("tag", &tags)
if err != nil {
@ -30,7 +30,13 @@ func (r *tagRepository) Find(ctx context.Context, kind, name string) (*models.Ta
return nil, repositories.ErrNotFound
}
return tags[0], nil
for _, tag := range tags {
if tag.Kind == kind && tag.Name == name {
return tag, nil
}
}
return nil, repositories.ErrNotFound
}
func (r *tagRepository) List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error) {

30
go.mod

@ -4,41 +4,35 @@ go 1.12
require (
github.com/99designs/gqlgen v0.9.0
github.com/agnivade/levenshtein v1.0.1
github.com/beorn7/perks v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e
github.com/go-ini/ini v1.35.0
github.com/go-ini/ini v1.35.0 // indirect
github.com/go-sql-driver/mysql v1.4.0
github.com/golang/protobuf v1.3.1 // indirect
github.com/google/go-cmp v0.3.0 // indirect
github.com/gorilla/websocket v1.4.0
github.com/gorilla/websocket v1.4.0 // indirect
github.com/graph-gophers/dataloader v0.0.0-20180104184831-78139374585c
github.com/hashicorp/golang-lru v0.5.0
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0
github.com/lib/pq v1.1.1 // indirect
github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747
github.com/opentracing/opentracing-go v1.0.2
github.com/pkg/errors v0.8.1
github.com/pmezard/go-difflib v1.0.0
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 // indirect
github.com/prometheus/client_golang v0.9.1
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
github.com/prometheus/common v0.4.0 // indirect
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e // indirect
github.com/sirupsen/logrus v1.2.0
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/stretchr/testify v1.3.0
github.com/tidwall/pretty v1.0.0 // indirect
github.com/urfave/cli v1.20.0
github.com/vektah/dataloaden v0.3.0
github.com/vektah/gqlparser v1.1.2
go.mongodb.org/mongo-driver v1.0.3
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190514140710-3ec191127204
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
golang.org/x/text v0.3.0
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd
google.golang.org/appengine v1.1.0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/appengine v1.1.0 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
gopkg.in/yaml.v2 v2.2.2
)

57
go.sum

@ -1,14 +1,5 @@
github.com/99designs/gqlgen v0.5.1 h1:cRsbpZgX83PrXb0/hj5EkNdmaVN4l/Eii81Z8LCexgY=
github.com/99designs/gqlgen v0.5.1/go.mod h1:KSQDfLlTTGmzlRgLGm6HeKKKo598l5E2svEM6Nz2Jnw=
github.com/99designs/gqlgen v0.8.0 h1:ZBteuSgeeFwn+mztjDqQZQdVa9pqslmPnDeCovcnc0Y=
github.com/99designs/gqlgen v0.8.0/go.mod h1:st7qHA6ssU3uRZkmv+wzrzgX4srvIqEIdE5iuRW8GhE=
github.com/99designs/gqlgen v0.8.3 h1:I6bMglXNKkn4KlvkSMzqZw53e1N2FF9Gud4NmsOxqiA=
github.com/99designs/gqlgen v0.8.3/go.mod h1:aLyJw9xUgdJxZ8EqNQxo2pGFhXXJ/hq8t7J4yn8TgI4=
github.com/99designs/gqlgen v0.9.0 h1:g1arBPML74Vqv0L3Q+TqIhGXLspV+2MYtRLkBxuZrlE=
github.com/99designs/gqlgen v0.9.0/go.mod h1:HrrG7ic9EgLPsULxsZh/Ti+p0HNWgR3XRuvnD0pb5KY=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/agnivade/levenshtein v1.0.0 h1:q+77q31bLT5jhN3BHKA1276nUEdbz7XjDa0o5dRKcZ0=
github.com/agnivade/levenshtein v1.0.0/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -18,14 +9,11 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e h1:hmrbHva/wKD6X4M7pgHVbg/KfVV1wwgr1NEkQaVD3rU=
github.com/globalsign/mgo v0.0.0-20180403085842-f76e4f9da92e/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
@ -33,18 +21,20 @@ github.com/go-ini/ini v1.35.0 h1:D/my3+xOfqZMkJpciRcyqU7XMBUgiZa9qXjZIa8uv2k=
github.com/go-ini/ini v1.35.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@ -56,6 +46,8 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0 h1:5B0uxl2lzNRVkJVg+uGHxWtRt4C0Wjc6kJKo5XYx8xE=
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -64,6 +56,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/minio/minio-go v0.0.0-20180409193742-3d2d02921f05 h1:YEyFCqmHcmbcTLqZ7yEAg7VIUat6PuDzGbIctZy888k=
@ -72,7 +68,6 @@ github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747 h1:eQox4Rh4ew
github.com/mitchellh/go-homedir v0.0.0-20161203194507-b8bc1bf76747/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@ -84,28 +79,24 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e h1:zK8d1aZ+gw/Ne4uMfZTFRxj08PUOp+gGwm4HWUeGI1k=
github.com/prometheus/procfs v0.0.0-20190517135640-51af30a78b0e/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -116,52 +107,42 @@ github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/vektah/dataloaden v0.2.0/go.mod h1:vxM6NuRlgiR0M6wbVTJeKp9vQIs81ZMfCYO+4yq/jbE=
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser v0.0.0-20180831041411-14e83ae06ec1 h1:FYOXUtr3sYR9shto7Q/aQ1B0Onyk77aws9wGOORiz+I=
github.com/vektah/gqlparser v0.0.0-20180831041411-14e83ae06ec1/go.mod h1:K4QdSSpS2XiHHwzb18kWh3iBljB8rLC8okGXsnQy3Nc=
github.com/vektah/gqlparser v1.1.0 h1:3668p2gUlO+PiS81x957Rpr3/FPRWG6cxgCXAvTS1hw=
github.com/vektah/gqlparser v1.1.0/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/vektah/dataloaden v0.3.0 h1:ZfVN2QD6swgvp+tDqdH/OIT/wu3Dhu0cus0k5gIZS84=
github.com/vektah/dataloaden v0.3.0/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U=
github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
golang.org/x/crypto v0.0.0-20180411161317-d6449816ce06 h1:EOqG0JqGlLr+punVB69jvWCv/ErZKGlC7PMdyHfv+Bc=
golang.org/x/crypto v0.0.0-20180411161317-d6449816ce06/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180404174746-b3c676e531a6/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180416171110-a35a21de978d h1:O2P57H5Cc+d+DJos+iweraI9rmzMYwV+45vkZdDY0Oo=
golang.org/x/net v0.0.0-20180416171110-a35a21de978d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI=
golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180416112224-2f57af4873d0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5 h1:mzjBh+S5frKOsOBobWIMAbXavqjmgO17k/2puhcFR94=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1 h1:dzEuQYa6+a3gROnSlgly5ERUm4SZKJt+dh+4iSbO+bI=
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw=
golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

4
graph2/complexity.go

@ -2,9 +2,9 @@ package graph2
import (
"git.aiterp.net/rpdata/api/graph2/graphcore"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/channels"
"git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models/files"
"git.aiterp.net/rpdata/api/models/logs"
"git.aiterp.net/rpdata/api/models/posts"
@ -22,7 +22,7 @@ func complexity() (cr graphcore.ComplexityRoot) {
cr.Query.Character = func(childComplexity int, id *string, nick *string) int {
return childComplexity + findComplexity
}
cr.Query.Characters = func(childComplexity int, filter *characters.Filter) int {
cr.Query.Characters = func(childComplexity int, filter *models.CharacterFilter) int {
return childComplexity + listComplexity
}
cr.Query.Channel = func(childComplexity int, name string) int {

2
graph2/gqlgen.yml

@ -20,7 +20,7 @@ models:
Character:
model: git.aiterp.net/rpdata/api/models.Character
CharactersFilter:
model: git.aiterp.net/rpdata/api/models/characters.Filter
model: git.aiterp.net/rpdata/api/models.CharacterFilter
Channel:
model: git.aiterp.net/rpdata/api/models.Channel
ChannelsFilter:

6
graph2/graph.go

@ -27,15 +27,15 @@ func (r *rootResolver) Query() graphcore.QueryResolver {
}
func (r *rootResolver) Mutation() graphcore.MutationResolver {
return resolvers.MutationResolver
return resolvers.MutationResolver(r.s)
}
func (r *rootResolver) Subscription() graphcore.SubscriptionResolver {
return resolvers.SubscriptionResolver
return resolvers.SubscriptionResolver(r.s)
}
func (r *rootResolver) Log() graphcore.LogResolver {
return &types.LogResolver
return types.LogResolver(r.s)
}
func (r *rootResolver) Comment() graphcore.CommentResolver {

157
graph2/resolvers/character.go

@ -3,69 +3,41 @@ package resolvers
import (
"context"
"errors"
"strings"
"git.aiterp.net/rpdata/api/graph2/graphcore"
"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/models/characters"
"git.aiterp.net/rpdata/api/models/logs"
)
// Queries
func (r *queryResolver) Character(ctx context.Context, id *string, nick *string) (*models.Character, error) {
if id != nil {
character, err := characters.FindID(*id)
if err != nil {
return nil, err
}
return &character, nil
return r.s.Characters.Find(ctx, *id)
} else if nick != nil {
character, err := characters.FindNick(*nick)
if err != nil {
return nil, err
}
return &character, nil
return r.s.Characters.FindNick(ctx, *nick)
} else {
return nil, errors.New("You must specify either an ID or a nick")
}
}
func (r *queryResolver) Characters(ctx context.Context, filter *characters.Filter) ([]*models.Character, error) {
characters, err := characters.List(filter)
if err != nil {
return nil, err
func (r *queryResolver) Characters(ctx context.Context, filter *models.CharacterFilter) ([]*models.Character, error) {
if filter == nil {
filter = &models.CharacterFilter{}
}
characters2 := make([]*models.Character, len(characters))
for i := range characters {
characters2[i] = &characters[i]
}
return characters2, nil
return r.s.Characters.List(ctx, *filter)
}
// Mutations
func (r *mutationResolver) AddCharacter(ctx context.Context, input graphcore.CharacterAddInput) (*models.Character, error) {
token := auth.TokenFromContext(ctx)
if !token.Permitted("member", "character.add") {
return nil, errors.New("You are not permitted to add characters")
}
if len(input.Name) < 2 || len(input.Nick) < 2 {
return nil, errors.New("You need to provide a name and a nick (min length: 2)")
author := ""
if input.Author != nil {
author = *input.Author
}
shortName := ""
if input.ShortName != nil {
shortName = *input.ShortName
} else {
shortName = strings.SplitN(input.Name, " ", 2)[0]
}
description := ""
@ -73,122 +45,21 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input graphcore.Cha
description = *input.Description
}
author := token.UserID
if input.Author != nil && *input.Author != author {
if !token.Permitted("character.add") {
return nil, errors.New("You are only permitted to add your own characters")
}
author = *input.Author
}
logs.ScheduleFullUpdate()
character, err := characters.Add(input.Nick, input.Name, shortName, author, description)
if err != nil {
return nil, errors.New("Adding character failed: " + err.Error())
}
go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character)
return &character, nil
return r.s.Characters.Create(ctx, input.Nick, input.Name, shortName, author, description)
}
func (r *mutationResolver) AddCharacterNick(ctx context.Context, input graphcore.CharacterNickInput) (*models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return nil, errors.New("Character not found")
}
if len(input.Nick) < 2 {
return nil, errors.New("You need to provide a valid nick (min length: 2)")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return nil, errors.New("You are not permitted to edit this character")
}
logs.ScheduleFullUpdate()
character, err = characters.AddNick(character, input.Nick)
if err != nil {
return nil, errors.New("Failed to add nick: " + err.Error())
}
go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return &character, nil
return r.s.Characters.AddNick(ctx, input.ID, input.Nick)
}
func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input graphcore.CharacterNickInput) (*models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return nil, errors.New("Character not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return nil, errors.New("You are not permitted to edit this character")
}
character, err = characters.RemoveNick(character, input.Nick)
if err != nil {
return nil, errors.New("Failed to remove nick: " + err.Error())
}
go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return &character, nil
return r.s.Characters.RemoveNick(ctx, input.ID, input.Nick)
}
func (r *mutationResolver) EditCharacter(ctx context.Context, input graphcore.CharacterEditInput) (*models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return nil, errors.New("Character not found")
}
if input.Name != nil && len(*input.Name) < 2 {
return nil, errors.New("You need to provide a valid name (min length: 2)")
}
if input.ShortName != nil && len(*input.ShortName) < 2 {
return nil, errors.New("You need to provide a valid short name (min length: 2)")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.edit") {
return nil, errors.New("You are not permitted to edit this character")
}
character, err = characters.Edit(character, input.Name, input.ShortName, input.Description)
if err != nil {
return nil, errors.New("Failed to edit character: " + err.Error())
}
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return &character, nil
return r.s.Characters.Update(ctx, input.ID, input.Name, input.ShortName, input.Description)
}
func (r *mutationResolver) RemoveCharacter(ctx context.Context, input graphcore.CharacterRemoveInput) (*models.Character, error) {
character, err := characters.FindID(input.ID)
if err != nil {
return nil, errors.New("Character not found")
}
token := auth.TokenFromContext(ctx)
if !token.PermittedUser(character.Author, "member", "character.remove") {
return nil, errors.New("You are not permitted to remove this character")
}
character, err = characters.Remove(character)
if err != nil {
return nil, errors.New("Failed to remove character: " + err.Error())
}
go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character)
return &character, nil
return r.s.Characters.Delete(ctx, input.ID)
}

14
graph2/resolvers/resolvers.go

@ -6,16 +6,20 @@ import (
)
type queryResolver struct{ s *services.Bundle }
type mutationResolver struct{}
type subscriptionResolver struct{}
type mutationResolver struct{ s *services.Bundle }
type subscriptionResolver struct{ s *services.Bundle }
// QueryResolver has all the queries
func QueryResolver(s *services.Bundle) graphcore.QueryResolver {
return &queryResolver{s: s}
}
// MutationResolver brings the mutagens.
var MutationResolver *mutationResolver
// MutationResolver brings the radioactive goop
func MutationResolver(s *services.Bundle) graphcore.MutationResolver {
return &mutationResolver{s: s}
}
// SubscriptionResolver has the real-time magic.
var SubscriptionResolver *subscriptionResolver
func SubscriptionResolver(s *services.Bundle) graphcore.SubscriptionResolver {
return &subscriptionResolver{s: s}
}

28
graph2/types/log.go

@ -3,13 +3,16 @@ package types
import (
"context"
"errors"
"git.aiterp.net/rpdata/api/services"
"git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/posts"
)
type logResolver struct{}
type logResolver struct {
characters *services.CharacterService
}
func (r *logResolver) Channel(ctx context.Context, log *models.Log) (*models.Channel, error) {
loader := loader.FromContext(ctx)
@ -21,22 +24,13 @@ func (r *logResolver) Channel(ctx context.Context, log *models.Log) (*models.Cha
}
func (r *logResolver) Characters(ctx context.Context, log *models.Log) ([]*models.Character, error) {
loader := loader.FromContext(ctx)
if loader == nil {
return nil, errors.New("no loader")
if len(log.CharacterIDs) == 0 {
return []*models.Character{}, nil
}
characters, err := loader.Characters("id", log.CharacterIDs...)
if err != nil {
return nil, err
}
characters2 := make([]*models.Character, len(characters))
for i := range characters {
characters2[i] = &characters[i]
}
return characters2, nil
return r.characters.List(ctx, models.CharacterFilter{
IDs: log.CharacterIDs,
})
}
func (r *logResolver) Posts(ctx context.Context, log *models.Log, kinds []string) ([]*models.Post, error) {
@ -54,4 +48,6 @@ func (r *logResolver) Posts(ctx context.Context, log *models.Log, kinds []string
}
// LogResolver is a resolver
var LogResolver logResolver
func LogResolver(s *services.Bundle) *logResolver {
return &logResolver{characters: s.Characters}
}

3
internal/auth/permitted.go

@ -2,6 +2,7 @@ package auth
import (
"context"
"log"
"reflect"
"git.aiterp.net/rpdata/api/models"
@ -43,6 +44,8 @@ func CheckPermission(ctx context.Context, op string, obj interface{}) error {
authorized = token.PermittedUser(v.Author, "member", "story."+op)
case *models.User:
authorized = token.Permitted("user." + op)
default:
log.Panicf("Invalid model %T: %#+v", v, v)
}
if !authorized {

4
models/changekeys/one.go

@ -26,6 +26,10 @@ func One(object interface{}) models.ChangeKey {
id := ""
v := reflect.ValueOf(object)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if f := v.FieldByName("ID"); f.Kind() == reflect.String {
id = f.String()
} else if f = v.FieldByName("Name"); f.Kind() == reflect.String {

16
models/changes/submit.go

@ -55,34 +55,50 @@ func Submit(model, op, author string, listed bool, keys []models.ChangeKey, obje
switch object := object.(type) {
case models.Log:
change.Logs = append(change.Logs, object)
case *models.Log:
change.Logs = append(change.Logs, *object)
case []models.Log:
change.Logs = append(change.Logs, object...)
case models.Character:
change.Characters = append(change.Characters, object)
case *models.Character:
change.Characters = append(change.Characters, *object)
case []models.Character:
change.Characters = append(change.Characters, object...)
case models.Channel:
change.Channels = append(change.Channels, object)
case *models.Channel:
change.Channels = append(change.Channels, *object)
case []models.Channel:
change.Channels = append(change.Channels, object...)
case models.Post:
change.Posts = append(change.Posts, object)
case *models.Post:
change.Posts = append(change.Posts, *object)
case []models.Post:
change.Posts = append(change.Posts, object...)
case models.Story:
change.Stories = append(change.Stories, object)
case *models.Story:
change.Stories = append(change.Stories, *object)
case []models.Story:
change.Stories = append(change.Stories, object...)
case models.Tag:
change.Tags = append(change.Tags, object)
case *models.Tag:
change.Tags = append(change.Tags, *object)
case []models.Tag:
change.Tags = append(change.Tags, object...)
case models.Chapter:
change.Chapters = append(change.Chapters, object)
case *models.Chapter:
change.Chapters = append(change.Chapters, *object)
case []models.Chapter:
change.Chapters = append(change.Chapters, object...)
case models.Comment:
change.Comments = append(change.Comments, object)
case *models.Comment:
change.Comments = append(change.Comments, *object)
case []models.Comment:
change.Comments = append(change.Comments, object...)
default:

19
models/character.go

@ -34,4 +34,21 @@ func (character *Character) HasNick(nick string) bool {
// ChangeObject in GQL.
func (*Character) IsChangeObject() {
panic("this method is a dummy, and so is its caller")
}
}
// CharacterFilter is a filter for character listing.
type CharacterFilter struct {
IDs []string
Nicks []string
Names []string
Author *string
Search *string
Limit int
}
// CharacterUpdate is an update for characters.
type CharacterUpdate struct {
Name *string
ShortName *string
Description *string
}

2
models/tag.go

@ -63,7 +63,7 @@ func (e TagKind) MarshalGQL(w io.Writer) {
fmt.Fprint(w, "\""+string(e), "\"")
}
// TagFilter is a filter of tags.
// TagFilter is a filter for tag listing.
type TagFilter struct {
Kind *TagKind `bson:"kind,omitempty"`
}

14
models/tags/db.go

@ -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")
})
}

26
models/tags/list.go

@ -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
}

19
repositories/character.go

@ -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
}

3
repositories/repository.go

@ -4,7 +4,8 @@ import "errors"
// A Bundle is a set of repositories.
type Bundle struct {
Tags TagRepository
Characters CharacterRepository
Tags TagRepository
}
// ErrNotFound should be returned instead of any database-specific not found error.

2
repositories/tag.go

@ -8,6 +8,6 @@ import (
// TagRepository is an interface for a database using logs.
type TagRepository interface {
Find(ctx context.Context, kind, name string) (*models.Tag, error)
Find(ctx context.Context, kind models.TagKind, name string) (*models.Tag, error)
List(ctx context.Context, filter models.TagFilter) ([]*models.Tag, error)
}

207
services/characters.go

@ -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
}

224
services/loaders/characterloader_gen.go

@ -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)
}

53
services/loaders/loaders.go

@ -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
},
}
}

14
services/services.go

@ -1,10 +1,14 @@
package services
import "git.aiterp.net/rpdata/api/repositories"
import (
"git.aiterp.net/rpdata/api/repositories"
"git.aiterp.net/rpdata/api/services/loaders"
)
// A Bundle contains all services, like a bean bag in the more caffeinated language family.
// A Bundle contains all services.
type Bundle struct {
Tags *TagService
Tags *TagService
Characters *CharacterService
}
// NewBundle creates a new bundle.
@ -12,6 +16,10 @@ func NewBundle(repos *repositories.Bundle) *Bundle {
bundle := &Bundle{}
bundle.Tags = &TagService{tags: repos.Tags}
bundle.Characters = &CharacterService{
characters: repos.Characters,
loader: loaders.CharacterLoaderFromRepository(repos.Characters),
}
return bundle
}

2
services/tags.go

@ -13,7 +13,7 @@ type TagService struct {
}
// FindTag finds one tag.
func (s *TagService) FindTag(ctx context.Context, source, id string) (*models.Tag, error) {
func (s *TagService) FindTag(ctx context.Context, source models.TagKind, id string) (*models.Tag, error) {
return s.tags.Find(ctx, source, id)
}

7
tools.go

@ -0,0 +1,7 @@
//+build tools
package main
import (
_ "github.com/vektah/dataloaden"
)
Loading…
Cancel
Save