Docs: Added sequence diagrams for web interface lifecycle

For now only startup and reconnect to server.
This commit is contained in:
Gina Häußge 2017-06-30 18:28:49 +02:00
parent ed6c01c12a
commit 5fc194accd
4 changed files with 213 additions and 30 deletions

View file

@ -33,7 +33,7 @@ needs_sphinx = '1.3'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['codeblockext', 'onlineinclude', 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinxcontrib.httpdomain',
'sphinx.ext.napoleon']
'sphinx.ext.napoleon', 'sphinxcontrib.mermaid']
todo_include_todos = True
# Add any paths that contain templates here, relative to this directory.

View file

@ -299,6 +299,130 @@ on your view model, taking a list of all bound view models:
});
})
.. _sec-plugins-viewmodels-livecycle:
Lifecycle diagrams
------------------
.. _sec-plugins-viewmodels-startup:
Web interface startup
~~~~~~~~~~~~~~~~~~~~~
.. mermaid::
sequenceDiagram
participant Main
participant onServerConnect
participant fetchSettings
participant bindViewModels
participant DataUpdater
participant LoginStateViewModel
Note right of DataUpdater: connectCallback = undefined
activate Main
Main->>+DataUpdater: connect
Note right of DataUpdater: initialized = false
DataUpdater-->>Main: ok
deactivate Main
DataUpdater->>DataUpdater: asynchronous connect to server...
activate DataUpdater
Note right of DataUpdater: store any callbacks instead of triggering (e.g. onServerConnect, fromHistoryData, fromCurrentData, ...)
DataUpdater-X+Main: done
deactivate DataUpdater
deactivate DataUpdater
Main->>+DataUpdater: connectCallback = onServerConnect
Note right of DataUpdater: connectCallback = onServerConnect
DataUpdater-->>-Main: ok
Main->>+onServerConnect: call
onServerConnect->>+LoginStateViewModel: passiveLogin
LoginStateViewModel-->>onServerConnect: ok
onServerConnect-->>Main: ok
deactivate onServerConnect
deactivate Main
LoginStateViewModel->>+LoginStateViewModel: asynchronous passive login
Note over Main,LoginStateViewModel: Session available!
LoginStateViewModel-X+onServerConnect: done
deactivate LoginStateViewModel
deactivate LoginStateViewModel
onServerConnect->>+DataUpdater: initialized
Note right of DataUpdater: initialized = true
DataUpdater->DataUpdater: trigger stored callbacks
DataUpdater-->>-onServerConnect: ok
onServerConnect-X+Main: done
deactivate onServerConnect
Main->>+fetchSettings: call
Note right of fetchSettings: trigger onStartup
fetchSettings-->>Main: ok
deactivate Main
fetchSettings->>+fetchSettings: asynchronous settings fetch
fetchSettings->>+bindViewModels: call
loop for each view model
bindViewModels->bindViewModels: trigger onBeforeBinding
bindViewModels->bindViewModels: trigger onBoundTo
bindViewModels->bindViewModels: trigger onAfterBinding
end
bindViewModels->bindViewModels: trigger onAllBound
opt User is logged in
bindViewModels->>+LoginStateViewModel: onAllBound
LoginStateViewModel->LoginStateViewModel: trigger onUserLoggedIn
LoginStateViewModel-->>-bindViewModels: ok
end
bindViewModels->bindViewModels: trigger onStartupComplete
bindViewModels-->>-fetchSettings: ok
deactivate fetchSettings
deactivate fetchSettings
.. _sec-plugins-viewmodels-reconnect:
Web interface reconnect
~~~~~~~~~~~~~~~~~~~~~~~
.. mermaid::
sequenceDiagram
participant onServerConnect
participant DataUpdater
participant LoginStateViewModel
activate DataUpdater
DataUpdater->>DataUpdater: call connectCallback
DataUpdater->>+onServerConnect: call
onServerConnect-->>DataUpdater: ok
deactivate DataUpdater
onServerConnect->>+LoginStateViewModel: passiveLogin
LoginStateViewModel-->>onServerConnect: ok
deactivate onServerConnect
LoginStateViewModel->>+LoginStateViewModel: asynchronous passive login
Note over onServerConnect,LoginStateViewModel: Session available!
opt User is logged in
LoginStateViewModel->LoginStateViewModel: trigger onUserLoggedIn
end
activate onServerConnect
LoginStateViewModel-XonServerConnect: done
deactivate LoginStateViewModel
deactivate LoginStateViewModel
onServerConnect->>+DataUpdater: initialized
DataUpdater->DataUpdater: trigger stored callbacks
DataUpdater-->>onServerConnect: ok
deactivate DataUpdater
deactivate onServerConnect
.. seealso::
`OctoPrint's core viewmodels <https://github.com/foosel/OctoPrint/tree/devel/src/octoprint/static/js/app/viewmodels>`_

View file

@ -7,41 +7,99 @@ __copyright__ = "Copyright (C) 2015 Gina Häußge - Released under terms of the
import codecs
import urllib2
import requests
from contextlib import closing
from sphinx.directives import LiteralInclude, dedent_lines
from sphinx.directives.code import LiteralIncludeReader, LiteralInclude, dedent_lines, \
nodes, set_source_info, parselinenos, logger, container_wrapper
if False:
# For type annotation
from typing import Any, List # NOQA
cache = dict()
class OnlineIncludeReader(LiteralIncludeReader):
def read_file(self, filename, location=None):
# type: (unicode, Any) -> List[unicode]
global cache
try:
if filename in cache:
lines = cache[filename]
else:
with closing(requests.get(filename, stream=True)) as r:
r.encoding = self.encoding
lines = r.text.splitlines(True)
cache[filename] = lines
if 'tab-width' in self.options:
lines = [line.expandtabs(self.options['tab-width']) for line in lines]
return lines
except (IOError, OSError):
raise IOError(_('Include file %r not found or reading it failed') % filename)
except UnicodeError:
raise UnicodeError(_('Encoding %r used for reading included file %r seems to '
'be wrong, try giving an :encoding: option') %
(self.encoding, filename))
class OnlineIncludeDirective(LiteralInclude):
def read_with_encoding(self, filename, document, codec_info, encoding):
global cache
def run(self):
# type: () -> List[nodes.Node]
document = self.state.document
if not document.settings.file_insertion_enabled:
return [document.reporter.warning('File insertion disabled',
line=self.lineno)]
env = document.settings.env
# convert options['diff'] to absolute path
if 'diff' in self.options:
_, path = env.relfn2path(self.options['diff'])
self.options['diff'] = path
f = None
try:
if not self.arguments[0] in cache:
f = codecs.StreamReaderWriter(urllib2.urlopen(self.arguments[0]), codec_info[2],
codec_info[3], 'strict')
lines = f.readlines()
cache[self.arguments[0]] = lines
else:
lines = cache[self.arguments[0]]
location = self.state_machine.get_source_and_line(self.lineno)
url = self.arguments[0]
reader = OnlineIncludeReader(url, self.options, env.config)
text, lines = reader.read(location=location)
retnode = nodes.literal_block(text, text, source=url)
set_source_info(self, retnode)
if self.options.get('diff'): # if diff is set, set udiff
retnode['language'] = 'udiff'
elif 'language' in self.options:
retnode['language'] = self.options['language']
retnode['linenos'] = ('linenos' in self.options or
'lineno-start' in self.options or
'lineno-match' in self.options)
retnode['classes'] += self.options.get('class', [])
extra_args = retnode['highlight_args'] = {}
if 'emphasize-lines' in self.options:
hl_lines = parselinenos(self.options['emphasize-lines'], lines)
if any(i >= lines for i in hl_lines):
logger.warning('line number spec is out of range(1-%d): %r' %
(lines, self.options['emphasize-lines']),
location=location)
extra_args['hl_lines'] = [x + 1 for x in hl_lines if x < lines]
extra_args['linenostart'] = reader.lineno_start
if 'caption' in self.options:
caption = self.options['caption'] or self.arguments[0]
retnode = container_wrapper(self, retnode, caption)
# retnode will be note_implicit_target that is linked from caption and numref.
# when options['name'] is provided, it should be primary ID.
self.add_name(retnode)
return [retnode]
except Exception as exc:
return [document.reporter.warning(str(exc), line=self.lineno)]
lines = dedent_lines(lines, self.options.get('dedent'))
return lines
except (IOError, OSError, urllib2.URLError):
return [document.reporter.warning(
'Include file %r not found or reading it failed' % self.arguments[0],
line=self.lineno)]
except UnicodeError:
return [document.reporter.warning(
'Encoding %r used for reading included file %r seems to '
'be wrong, try giving an :encoding: option' %
(encoding, self.arguments[0]))]
finally:
if f is not None:
f.close()
def visit_onlineinclude(translator, node):
translator.visit_literal_block(node)
@ -53,4 +111,4 @@ def setup(app):
app.add_directive("onlineinclude", OnlineIncludeDirective)
handler = (visit_onlineinclude, depart_onlineinclude)
app.add_node(OnlineIncludeDirective, html=handler, latex=handler, text=handler)
app.add_node(OnlineIncludeDirective, html=handler, latex=handler, text=handler)

View file

@ -65,9 +65,10 @@ EXTRA_REQUIRES = dict(
"ddt",
# Documentation dependencies
"sphinx>=1.3,<1.4",
"sphinx>=1.6,<1.7",
"sphinxcontrib-httpdomain",
"sphinx_rtd_theme",
"sphinxcontrib-mermaid",
"sphinx_bootstrap_theme",
# PyPi upload related
"pypandoc"