diff options
| author | Chris McDonough <chrism@plope.com> | 2014-12-16 19:48:53 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2014-12-16 19:48:53 -0500 |
| commit | 27db38880d46b6f4345cf86766924de976e24177 (patch) | |
| tree | 8c5dcf10f1177e6b0fc212cd8fa0eea20ecf9f5d | |
| parent | b1fac53cd0c3b930aec90e27f4d19c5f785f52e2 (diff) | |
| parent | cc15bbf7de74f4cdfc676e34fa429d2658d1ddf6 (diff) | |
| download | pyramid-27db38880d46b6f4345cf86766924de976e24177.tar.gz pyramid-27db38880d46b6f4345cf86766924de976e24177.tar.bz2 pyramid-27db38880d46b6f4345cf86766924de976e24177.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
45 files changed, 10997 insertions, 423 deletions
diff --git a/.travis.yml b/.travis.yml index 4ca998c42..5a205b268 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,21 @@ # Wire up travis language: python -python: - - 2.6 - - 2.7 - - pypy - - 3.2 - - 3.3 - - 3.4 - - pypy3 +env: + - TOXENV=py26 + - TOXENV=py27 + - TOXENV=py32 + - TOXENV=py33 + - TOXENV=py34 + - TOXENV=pypy + - TOXENV=pypy3 + - TOXENV=cover -install: python setup.py dev +install: + - travis_retry pip install tox -script: python setup.py test -q +script: + - travis_retry tox notifications: email: diff --git a/CHANGES.txt b/CHANGES.txt index b5d08c8ff..46c331268 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,9 @@ Next release Features -------- +- Added support / testing for 'pypy3' under Tox and Travis. + See https://github.com/Pylons/pyramid/pull/1469 + - Cache busting for static resources has been added and is available via a new argument to ``pyramid.config.Configurator.add_static_view``: ``cachebust``. See https://github.com/Pylons/pyramid/pull/1380 @@ -12,8 +15,8 @@ Features parameter to assist with includeable packages that wish to resolve resources relative to the package in which the ``Configurator`` was created. This is especially useful for addons that need to load asset specs from - settings, in which case it is natural for a user to define things relative - to their own packages. + settings, in which case it is may be natural for a developer to define + imports or assets relative to the top-level package. See https://github.com/Pylons/pyramid/pull/1337 - Added line numbers to the log formatters in the scaffolds to assist with @@ -24,12 +27,38 @@ Features ``431 Request Header Fields Too Large`` in ``pyramid.httpexceptions``. See https://github.com/Pylons/pyramid/pull/1372/files +- The ``pshell`` script will now load a ``PYTHONSTARTUP`` file if one is + defined in the environment prior to launching the interpreter. + See https://github.com/Pylons/pyramid/pull/1448 + - Make it simple to define notfound and forbidden views that wish to use the default exception-response view but with altered predicates and other configuration options. The ``view`` argument is now optional in ``config.add_notfound_view`` and ``config.add_forbidden_view``.. See https://github.com/Pylons/pyramid/issues/494 +- Greatly improve the readability of the ``pcreate`` shell script output. + See https://github.com/Pylons/pyramid/pull/1453 + +- Improve robustness to timing attacks in the ``AuthTktCookieHelper`` and + the ``SignedCookieSessionFactory`` classes by using the stdlib's + ``hmac.compare_digest`` if it is available (such as Python 2.7.7+ and 3.3+). + See https://github.com/Pylons/pyramid/pull/1457 + +- Assets can now be overidden by an absolute path on the filesystem when using + the ``config.override_asset`` API. This makes it possible to fully support + serving up static content from a mutable directory while still being able + to use the ``request.static_url`` API and ``config.add_static_view``. + Previously it was not possible to use ``config.add_static_view`` with an + absolute path **and** generate urls to the content. This change replaces + the call, ``config.add_static_view('/abs/path', 'static')``, with + ``config.add_static_view('myapp:static', 'static')`` and + ``config.override_asset(to_override='myapp:static/', + override_with='/abs/path/')``. The ``myapp:static`` asset spec is completely + made up and does not need to exist - it is used for generating urls + via ``request.static_url('myapp:static/foo.png')``. + See https://github.com/Pylons/pyramid/issues/1252 + Bug Fixes --------- @@ -67,6 +96,16 @@ Bug Fixes - Fix a failing unittest caused by differing mimetypes across various OSs. See https://github.com/Pylons/pyramid/issues/1405 +- Fix route generation for static view asset specifications having no path. + See https://github.com/Pylons/pyramid/pull/1377 + +Deprecations +------------ + +- Renamed the ``principal`` argument to ``pyramid.security.remember()`` to + ``userid`` in order to clarify its intended purpose. + See https://github.com/Pylons/pyramid/pull/1399 + Docs ---- @@ -77,6 +116,10 @@ Docs - Clarify a previously-implied detail of the ``ISession.invalidate`` API documentation. +- Improve and clarify the documentation on what Pyramid defines as a + ``principal`` and a ``userid`` in its security APIs. + See https://github.com/Pylons/pyramid/pull/1399 + Scaffolds --------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index c77d3e92c..9c2191f3b 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -232,3 +232,7 @@ Contributors - Amit Mane, 2014/01/23 - Fenton Travers, 2014/05/06 + +- Randall Leeds, 2014/11/11 + +- Hugo Branquinho, 2014/11/25 diff --git a/HACKING.txt b/HACKING.txt index e3afbf241..16c17699c 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -31,7 +31,7 @@ By Hand $ cd hack-on-pyramid # Configure remotes such that you can pull changes from the Pyramid # repository into your local repository. - $ git remote add upstream https://github.com:Pylons/pyramid.git + $ git remote add upstream https://github.com/Pylons/pyramid.git # fetch and merge changes from upstream into master $ git fetch upstream $ git merge upstream/master diff --git a/README.rst b/README.rst index a3458028b..adf7eea5e 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,13 @@ Pyramid ======= +.. image:: https://travis-ci.org/Pylons/pyramid.png?branch=master + :target: https://travis-ci.org/Pylons/pyramid + +.. image:: https://readthedocs.org/projects/pyramid/badge/?version=master + :target: http://docs.pylonsproject.org/projects/pyramid/en/master/ + :alt: Documentation Status + Pyramid is a small, fast, down-to-earth, open source Python web framework. It makes real-world web application development and deployment more fun, more predictable, and more productive. diff --git a/RELEASING.txt b/RELEASING.txt index 553d2dcf2..0adef552c 100644 --- a/RELEASING.txt +++ b/RELEASING.txt @@ -26,6 +26,9 @@ Releasing Pyramid - Copy relevant changes (delta bug fixes) from CHANGES.txt to docs/whatsnew-X.X (if it's a major release). +- update README.rst to use correct versions of badges and URLs according to + each branch and context, i.e., RTD "latest" == GitHub/Travis "1.x-branch". + - Make sure docs render OK:: $ cd docs @@ -125,7 +125,10 @@ Future - 1.7: Change ``pyramid.authentication.AuthTktAuthenticationPolicy`` default ``hashalg`` to ``sha512``. -- 1.8 Remove set_request_property. +- 1.8: Remove set_request_property. + +- 1.9: Remove extra code enabling ``pyramid.security.remember(principal=...)`` + and force use of ``userid``. Probably Bad Ideas ------------------ diff --git a/docs/_static/pyramid_request_processing.graffle b/docs/_static/pyramid_request_processing.graffle new file mode 100644 index 000000000..71319610b --- /dev/null +++ b/docs/_static/pyramid_request_processing.graffle @@ -0,0 +1,9748 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>ActiveLayerIndex</key> + <integer>0</integer> + <key>ApplicationVersion</key> + <array> + <string>com.omnigroup.OmniGrafflePro</string> + <string>139.18.0.187838</string> + </array> + <key>AutoAdjust</key> + <true/> + <key>BackgroundGraphic</key> + <dict> + <key>Bounds</key> + <string>{{0, 0}, {576, 733}}</string> + <key>Class</key> + <string>SolidGraphic</string> + <key>FontInfo</key> + <dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>ID</key> + <integer>2</integer> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + </dict> + </dict> + <key>BaseZoom</key> + <integer>0</integer> + <key>CanvasOrigin</key> + <string>{0, 0}</string> + <key>ColumnAlign</key> + <integer>1</integer> + <key>ColumnSpacing</key> + <real>36</real> + <key>CreationDate</key> + <string>2014-11-18 08:33:33 +0000</string> + <key>Creator</key> + <string>Steve Piercy</string> + <key>DisplayScale</key> + <string>1 0/72 in = 1 0/72 in</string> + <key>GraphDocumentVersion</key> + <integer>8</integer> + <key>GraphicsList</key> + <array> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169389</integer> + </dict> + <key>ID</key> + <integer>169504</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{344.41667175292969, 402.88506673894034}</string> + <string>{375.5, 402.27232108797347}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169428</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169382</integer> + </dict> + <key>ID</key> + <integer>169433</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 459.27667544230695}</string> + <string>{238.5002713470962, 456.52468399152298}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169370</integer> + <key>Position</key> + <real>0.28820157051086426</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169383</integer> + </dict> + <key>ID</key> + <integer>169432</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 482.12574895537085}</string> + <string>{238.52297468463752, 508.35839132916635}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169370</integer> + <key>Position</key> + <real>0.5668826699256897</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{238.8333613077798, 284.99999999999994}, {105.66668701171875, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169425</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 authorization}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 412.15071036499205}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169426</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, 0.5}</string> + <string>{-0.49999999999999911, 0.49999999999999289}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 303.65604172230951}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169427</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 393.55704269887212}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169428</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response adapter}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 374.90099016834085}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169429</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 341.36561209044055}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169430</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.75000762939453, 322.26348241170439}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169431</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>ID</key> + <integer>169424</integer> + <key>Layer</key> + <integer>0</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169422</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169423</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 470.25295298442387}</string> + <string>{238.33861159880226, 482.4262543949045}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169370</integer> + <key>Position</key> + <real>0.42701038718223572</real> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 471.22620192028251}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169422</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewResponse}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169420</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169421</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{154.99998733539806, 128.68025330008533}</string> + <string>{239.83340199788393, 128.59152244387357}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169386</integer> + <key>Position</key> + <real>0.35945424437522888</real> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239.83340199788395, 117.31920169649808}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169420</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewRequest}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{102.1666056315114, 148.28868579864499}, {105.66669464111328, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169418</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 URL dispatch}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.1666056315114, 181.37798023223874}, {105.66669464111328, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169419</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 route predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169418</integer> + <integer>169419</integer> + <array/> + </array> + <key>ID</key> + <integer>169417</integer> + <key>Layer</key> + <integer>0</integer> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{102.16666158040482, 272}, {105.66666412353516, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169412</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view lookup}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16666158040482, 305.08929443359375}, {105.66666412353516, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169413</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169412</integer> + <integer>169413</integer> + <array/> + </array> + <key>ID</key> + <integer>169411</integer> + <key>Layer</key> + <integer>0</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169407</integer> + <key>Info</key> + <integer>7</integer> + </dict> + <key>ID</key> + <integer>169410</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{238.75000762939462, 430.80675844512831}</string> + <string>{207.66666666666765, 385.656005859375}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169426</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169407</integer> + <key>Info</key> + <integer>8</integer> + </dict> + <key>ID</key> + <integer>169409</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{239.33336141608385, 285.57837549845181}</string> + <string>{207.66666666666777, 353.07514659563753}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169425</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169381</integer> + </dict> + <key>ID</key> + <integer>169408</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 386.66442959065108}</string> + <string>{155.00000254313238, 422.21209462483216}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169407</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 353.07514659563753}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169407</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{-0.49211360058019871, -0.49251945318722434}</string> + <string>{-0.49211360058019871, 0.49470854679786669}</string> + <string>{0.4984227008620481, 0.48463479169597612}</string> + <string>{0.49842270086204898, -0.5}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view pipeline}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169380</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169399</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{154.9999936421724, 258.44082431579938}</string> + <string>{238.8333613077798, 258.45536063967575}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169372</integer> + <key>Position</key> + <real>0.51973581314086914</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{383.66662216186666, 130.51770718892479}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169393</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 internal process}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{383.66662216186666, 91.940789540609359}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169394</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 external process (middleware, tween)}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{383.66662216186666, 158.54998334248924}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169395</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{383.66662216186666, 186.58225949605369}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169396</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 callback}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{383.66662216186666, 63.908513387045019}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169397</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 event}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{370.9999504089372, 42.910746256511771}, {132.66667175292969, 184.08924865722656}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169398</integer> + <key>Magnets</key> + <array> + <string>{1, 0.5}</string> + <string>{1, -0.5}</string> + <string>{-1, 0.5}</string> + <string>{-1, -0.5}</string> + <string>{0.5, 1}</string> + <string>{-0.5, 1}</string> + <string>{0.5, -1}</string> + <string>{-0.5, -1}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Align</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 + +\f0\b\fs20 \cf0 Legend}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>TextPlacement</key> + <integer>0</integer> + </dict> + </array> + <key>ID</key> + <integer>169391</integer> + <key>Layer</key> + <integer>0</integer> + </dict> + <dict> + <key>Bounds</key> + <string>{{233.5000012715667, 20.000000000000934}, {116, 14}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>FitText</key> + <string>YES</string> + <key>Flow</key> + <string>Resize</string> + <key>FontInfo</key> + <dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>ID</key> + <integer>169390</integer> + <key>Layer</key> + <integer>0</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Pad</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\b\fs24 \cf0 <%Canvas%>}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>Wrap</key> + <string>NO</string> + </dict> + <dict> + <key>Bounds</key> + <string>{{375.5, 391}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169389</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 BeforeRender}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169418</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169386</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000170434049, 119.22767858295661}</string> + <string>{154.99995295206804, 148.28868579864499}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169378</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169378</integer> + </dict> + <key>ID</key> + <integer>169385</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 67.727678571434836}</string> + <string>{155.00000254313238, 96.18303707668386}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169377</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 509.6179466247504}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169384</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 497.23589324949899}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169383</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 finished callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 445.23589324949717}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169382</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 422.21209462483216}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169381</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 247.18303989230026}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169380</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 ContextFound}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 222.18303707668389}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169379</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 traversal}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 96.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169378</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{102.16667048136482, 45.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169377</integer> + <key>Layer</key> + <integer>0</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware ingress }</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169379</integer> + </dict> + <key>ID</key> + <integer>169373</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{154.99995295206804, 198.62202930450437}</string> + <string>{155.00000254313238, 222.18303707668389}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169419</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169412</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169372</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{154.9999936421724, 245.22767856643924}</string> + <string>{154.9999936421724, 272}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169379</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169407</integer> + </dict> + <key>ID</key> + <integer>169371</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{154.9999936421724, 322.33334350585938}</string> + <string>{155.00000254313238, 353.07514659563753}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169413</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9839935302734375}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169384</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169370</integer> + <key>Layer</key> + <integer>0</integer> + <key>Points</key> + <array> + <string>{155.00000254313238, 444.75673611958314}</string> + <string>{155.00000254313238, 509.6179466247504}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169381</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169444</integer> + <key>Info</key> + <integer>6</integer> + </dict> + <key>ID</key> + <integer>169503</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{272.4166717529298, 537.32234122436705}</string> + <string>{420.4999504089364, 515.08928491955714}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169494</integer> + <key>Info</key> + <integer>5</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169444</integer> + </dict> + <key>ID</key> + <integer>169502</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{272.50004831949906, 391.51558277923863}</string> + <string>{420.4999504089364, 472.78869058972316}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169493</integer> + <key>Info</key> + <integer>5</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169450</integer> + </dict> + <key>ID</key> + <integer>169501</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 592.81693102013151}</string> + <string>{239, 583.78422005970799}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169438</integer> + <key>Position</key> + <real>0.28820157051086426</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169451</integer> + </dict> + <key>ID</key> + <integer>169500</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 629.80996162681686}</string> + <string>{239, 640.78422005970981}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169438</integer> + <key>Position</key> + <real>0.5668826699256897</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{166.8333613077798, 391.51558277923863}, {105.66668701171875, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169493</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 authorization}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 518.66629314423074}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169494</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, 0.5}</string> + <string>{-0.49999999999999911, 0.49999999999999289}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 410.17162450154819}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169495</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 500.07262547811081}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169496</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response adapter}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 481.41657294757954}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169497</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 447.88119486967923}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169498</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 428.77906519094307}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169499</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>ID</key> + <integer>169492</integer> + <key>Layer</key> + <integer>1</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169490</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169491</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.166643778483959, 611.77452873049333}</string> + <string>{238.8333613077798, 611.77452873049333}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 600.50220798311784}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169490</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewResponse}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169488</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169489</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{82.999986314263907, 140.3328574622312}</string> + <string>{239.83340199788393, 141.59152244387357}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169454</integer> + <key>Position</key> + <real>0.35945424437522888</real> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239.83340199788395, 130.31920169649808}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169488</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewRequest}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{30.166605631511416, 166.28868579864499}, {105.66668701171875, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169486</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 URL dispatch}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166605631511416, 199.37798023223874}, {105.66668701171875, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169487</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 route predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169486</integer> + <integer>169487</integer> + <array/> + </array> + <key>ID</key> + <integer>169485</integer> + <key>Layer</key> + <integer>1</integer> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{420.5000406901047, 338.15028762817326}, {105.66668701171875, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169483</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view lookup}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.5000406901047, 371.23958206176701}, {105.66668701171875, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169484</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169483</integer> + <integer>169484</integer> + <array/> + </array> + <key>ID</key> + <integer>169482</integer> + <key>Layer</key> + <integer>1</integer> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{30.166661580404835, 335}, {105.66667175292969, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169480</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view lookup}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166661580404835, 368.08929443359375}, {105.66667175292969, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169481</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169480</integer> + <integer>169481</integer> + <array/> + </array> + <key>ID</key> + <integer>169479</integer> + <key>Layer</key> + <integer>1</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169475</integer> + <key>Info</key> + <integer>7</integer> + </dict> + <key>ID</key> + <integer>169478</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{166.75000762939462, 537.32234122436694}</string> + <string>{135.66666666666765, 485}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169494</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169475</integer> + <key>Info</key> + <integer>8</integer> + </dict> + <key>ID</key> + <integer>169477</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{167.33336141608385, 392.09395827769049}</string> + <string>{135.66666666666777, 452.41914073626253}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169493</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169449</integer> + </dict> + <key>ID</key> + <integer>169476</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 485.50842372576449}</string> + <string>{83.000002543132396, 548.10604731241608}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169475</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 452.41914073626253}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169475</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{-0.49211360058019871, -0.49251945318722434}</string> + <string>{-0.49211360058019871, 0.49470854679786669}</string> + <string>{0.4984227008620481, 0.48463479169597612}</string> + <string>{0.49842270086204898, -0.5}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view pipeline}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{51.333333333333314, 0}</string> + <string>{-0.66666666666662877, 58.666666666666686}</string> + <string>{0.66673293066804717, -58.666850540458825}</string> + <string>{-16.306719354194399, 0.26652623861849634}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169443</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169474</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{369.66666666666669, 541}</string> + <string>{404.00000000000023, 362}</string> + <string>{420.36749776329049, 302.42112495959606}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.75663</string> + <key>g</key> + <string>0.756618</string> + <key>r</key> + <string>0.75664</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{69.833333333332462, -0.72767857143483639}</string> + <string>{-0.66690523835279691, -51.044605218028948}</string> + <string>{0.66666666666674246, 51.044637362162291}</string> + <string>{-24.333271383961971, -0.13425428344572765}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169443</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169473</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{310.66666666666754, 118.72767857143484}</string> + <string>{399.33333333333417, 216.62202930450439}</string> + <string>{420.37955338188368, 301.45369961823752}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.75663</string> + <key>g</key> + <string>0.756618</string> + <key>r</key> + <string>0.75664</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{-3.9999491373696401, 78.910715080442856}</string> + <string>{92.666683130060392, 0.22547126950667007}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169490</integer> + <key>Info</key> + <integer>3</integer> + </dict> + <key>ID</key> + <integer>169472</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{473.33328247070392, 515.08928491955714}</string> + <string>{344.50002543131501, 611.77452873049333}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169444</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-32.166667938232536, 10.244050343831077}</string> + </array> + <key>ID</key> + <integer>169471</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{344.96346869509995, 346.26317428240412}</string> + <string>{389.8333346048999, 328.08928298950207}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169456</integer> + <key>Info</key> + <integer>3</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-28.500001271565793, 8.3333333333333144}</string> + </array> + <key>ID</key> + <integer>169470</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{344.98861594084059, 323.71068461220347}</string> + <string>{391.1666679382335, 313.6666666666664}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169455</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-40, 1.5446373167492311}</string> + </array> + <key>ID</key> + <integer>169469</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{344.9995533451783, 301.1612218744512}</string> + <string>{394.50000127156665, 299.00000000000045}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169442</integer> + <key>Info</key> + <integer>3</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{8.5833282470703125, -10.244596987647753}</string> + <string>{0, 0}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169457</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169468</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{272.41667175292969, 509.40064951817902}</string> + <string>{285.5, 503.81699882234847}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169496</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169448</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169467</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 300.27229985501288}</string> + <string>{238.34892458824362, 260.57913893040109}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169440</integer> + <key>Position</key> + <real>0.51973581314086914</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 214.61452811107179}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169460</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.756045</string> + <key>g</key> + <string>0.75004</string> + <key>r</key> + <string>0.994455</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 exception}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 130.51770718892479}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169461</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 internal process}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 91.940789540609359}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169462</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 external process (middleware, tween)}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 158.54998334248924}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169463</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 186.58225949605369}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169464</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 callback}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 63.908513387045019}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169465</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 event}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{406.9999504089372, 42.910746256511771}, {132.66667175292969, 207.81692504882812}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169466</integer> + <key>Magnets</key> + <array> + <string>{1, 0.5}</string> + <string>{1, -0.5}</string> + <string>{-1, 0.5}</string> + <string>{-1, -0.5}</string> + <string>{0.5, 1}</string> + <string>{-0.5, 1}</string> + <string>{0.5, -1}</string> + <string>{-0.5, -1}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Align</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 + +\f0\b\fs20 \cf0 Legend}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>TextPlacement</key> + <integer>0</integer> + </dict> + </array> + <key>ID</key> + <integer>169459</integer> + <key>Layer</key> + <integer>1</integer> + </dict> + <dict> + <key>Bounds</key> + <string>{{233.5000012715667, 20.000000000000934}, {116, 14}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>FitText</key> + <string>YES</string> + <key>Flow</key> + <string>Resize</string> + <key>FontInfo</key> + <dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>ID</key> + <integer>169458</integer> + <key>Layer</key> + <integer>1</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Pad</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\b\fs24 \cf0 <%Canvas%>}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>Wrap</key> + <string>NO</string> + </dict> + <dict> + <key>Bounds</key> + <string>{{285.5, 492.544677734375}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169457</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 BeforeRender}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83337529500417, 335.17855853126167}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169456</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 HTTPForbidden}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83337529500417, 312.54463200342093}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169455</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 PredicateMismatch}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169486</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169454</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000001850648417, 128.2276785555359}</string> + <string>{82.999949137370791, 166.28868579864502}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169446</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169446</integer> + </dict> + <key>ID</key> + <integer>169453</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 67.727678571434836}</string> + <string>{83.000002543132396, 105.18303707668386}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169445</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 671.51189931233432}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169452</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 629.51189931233432}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169451</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 finished callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 572.5118993123325}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169450</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 548.10604731241608}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169449</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 249.18303989230026}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169448</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 ContextFound}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 240.18303707668389}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169447</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 traversal}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 105.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169446</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 45.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169445</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware ingress }</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.49995040893634, 472.78869058972316}, {105.66666412353516, 42.300594329833984}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169444</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{-0.49999999999999956, -0.5}</string> + <string>{-0.49999999999999956, 0.5}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 notfound_view / forbidden_view / exception_view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.49995040893634, 290.66666666666691}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169443</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.756045</string> + <key>g</key> + <string>0.75004</string> + <key>r</key> + <string>0.994455</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 exception}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336512247806, 289.91071033477789}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169442</integer> + <key>Layer</key> + <integer>1</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 HTTPNotFound}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169447</integer> + </dict> + <key>ID</key> + <integer>169441</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{82.999949137370791, 216.62202930450437}</string> + <string>{83.000002543132396, 240.18303707668389}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169487</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169480</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169440</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 263.22767855635425}</string> + <string>{82.999997456869679, 335}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169447</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169475</integer> + </dict> + <key>ID</key> + <integer>169439</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 385.33334350585938}</string> + <string>{83.000002543132396, 452.41914073626253}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169481</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9839935302734375}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169452</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169438</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 571.15068879140836}</string> + <string>{83.000002543132396, 671.51189931233421}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169449</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.055999755859375}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169483</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169437</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{473.33328247070392, 313.2113088426139}</string> + <string>{473.33338419596407, 338.15028762817326}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169443</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840011596679688}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169444</integer> + </dict> + <key>ID</key> + <integer>169436</integer> + <key>Layer</key> + <integer>1</integer> + <key>Points</key> + <array> + <string>{473.33338359264764, 388.98363112285512}</string> + <string>{473.33328247070392, 472.78869058972316}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169484</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169358</integer> + <key>Info</key> + <integer>6</integer> + </dict> + <key>ID</key> + <integer>169359</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{272.4166717529298, 537.32234122436705}</string> + <string>{420.4999504089364, 515.08928491955714}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169206</integer> + <key>Info</key> + <integer>5</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169358</integer> + </dict> + <key>ID</key> + <integer>169360</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{272.50004831949906, 391.51558277923863}</string> + <string>{420.4999504089364, 472.78869058972316}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169205</integer> + <key>Info</key> + <integer>5</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169044</integer> + </dict> + <key>ID</key> + <integer>169130</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 592.81693102013151}</string> + <string>{239, 583.78422005970799}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169128</integer> + <key>Position</key> + <real>0.28820157051086426</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169045</integer> + </dict> + <key>ID</key> + <integer>169129</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 629.80996162681686}</string> + <string>{239, 640.78422005970981}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169128</integer> + <key>Position</key> + <real>0.5668826699256897</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{166.8333613077798, 391.51558277923863}, {105.66668701171875, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169205</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 authorization}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 518.66629314423074}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169206</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, 0.5}</string> + <string>{-0.49999999999999911, 0.49999999999999289}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 410.17162450154819}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169207</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{0.50000000000000089, -0.49999999999999645}</string> + <string>{-0.49526813868737474, -0.4689979626999552}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 decorators ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 500.07262547811081}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169208</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response adapter}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 481.41657294757954}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169209</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 447.88119486967923}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169210</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{166.75000762939453, 428.77906519094307}, {105.66666412353516, 18.656048080136394}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169211</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.637876</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view mapper ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>ID</key> + <integer>169204</integer> + <key>Layer</key> + <integer>2</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169085</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169086</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.166643778483959, 611.77452873049333}</string> + <string>{238.8333613077798, 611.77452873049333}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 600.50220798311784}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169085</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewResponse}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169083</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169084</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{82.999986314263907, 140.3328574622312}</string> + <string>{239.83340199788393, 141.59152244387357}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169048</integer> + <key>Position</key> + <real>0.35945424437522888</real> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239.83340199788395, 130.31920169649808}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169083</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 NewRequest}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{30.166605631511416, 166.28868579864499}, {105.66668701171875, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169081</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 URL dispatch}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166605631511416, 199.37798023223874}, {105.66668701171875, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169082</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 route predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169081</integer> + <integer>169082</integer> + <array/> + </array> + <key>ID</key> + <integer>169080</integer> + <key>Layer</key> + <integer>2</integer> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{420.5000406901047, 338.15028762817326}, {105.66668701171875, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169355</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view lookup}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.5000406901047, 371.23958206176701}, {105.66668701171875, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169356</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169355</integer> + <integer>169356</integer> + <array/> + </array> + <key>ID</key> + <integer>169354</integer> + <key>Layer</key> + <integer>2</integer> + </dict> + <dict> + <key>Class</key> + <string>TableGroup</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{30.166661580404835, 335}, {105.66667175292969, 33.08929443359375}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169075</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view lookup}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166661580404835, 368.08929443359375}, {105.66667175292969, 17.244049072265625}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169076</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 predicates}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + </array> + <key>GridH</key> + <array> + <integer>169075</integer> + <integer>169076</integer> + <array/> + </array> + <key>ID</key> + <integer>169074</integer> + <key>Layer</key> + <integer>2</integer> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169070</integer> + <key>Info</key> + <integer>7</integer> + </dict> + <key>ID</key> + <integer>169073</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{166.75000762939462, 537.32234122436694}</string> + <string>{135.66666666666765, 485}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169206</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169070</integer> + <key>Info</key> + <integer>8</integer> + </dict> + <key>ID</key> + <integer>169072</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{167.33336141608385, 392.09395827769049}</string> + <string>{135.66666666666777, 452.41914073626253}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.755269</string> + <key>g</key> + <string>0.755239</string> + <key>r</key> + <string>0.75529</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>11</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169205</integer> + <key>Info</key> + <integer>6</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169043</integer> + </dict> + <key>ID</key> + <integer>169071</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 485.50842372576449}</string> + <string>{83.000002543132396, 548.10604731241608}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169070</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 452.41914073626253}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169070</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{-0.49211360058019871, -0.49251945318722434}</string> + <string>{-0.49211360058019871, 0.49470854679786669}</string> + <string>{0.4984227008620481, 0.48463479169597612}</string> + <string>{0.49842270086204898, -0.5}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view pipeline}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{51.333333333333314, 0}</string> + <string>{-0.66666666666662877, 58.666666666666686}</string> + <string>{0.66673293066804717, -58.666850540458825}</string> + <string>{-16.306719354194399, 0.26652623861849634}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169344</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169345</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{369.66666666666669, 541}</string> + <string>{404.00000000000023, 362}</string> + <string>{420.36749776329049, 302.42112495959606}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.75663</string> + <key>g</key> + <string>0.756618</string> + <key>r</key> + <string>0.75664</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{69.833333333332462, -0.72767857143483639}</string> + <string>{-0.66690523835279691, -51.044605218028948}</string> + <string>{0.66666666666674246, 51.044637362162291}</string> + <string>{-24.333271383961971, -0.13425428344572765}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169344</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169346</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{310.66666666666754, 118.72767857143484}</string> + <string>{399.33333333333417, 216.62202930450439}</string> + <string>{420.37955338188368, 301.45369961823752}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.75663</string> + <key>g</key> + <string>0.756618</string> + <key>r</key> + <string>0.75664</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{-3.9999491373696401, 78.910715080442856}</string> + <string>{92.666683130060392, 0.22547126950667007}</string> + </array> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169085</integer> + <key>Info</key> + <integer>3</integer> + </dict> + <key>ID</key> + <integer>169361</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{473.33328247070392, 515.08928491955714}</string> + <string>{344.50002543131501, 611.77452873049333}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169358</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-32.166667938232536, 10.244050343831077}</string> + </array> + <key>ID</key> + <integer>169341</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{344.96346869509995, 346.26317428240412}</string> + <string>{389.8333346048999, 328.08928298950207}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169340</integer> + <key>Info</key> + <integer>3</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-28.500001271565793, 8.3333333333333144}</string> + </array> + <key>ID</key> + <integer>169337</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{344.98861594084059, 323.71068461220347}</string> + <string>{391.1666679382335, 313.6666666666664}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169336</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{31.999987284342428, -14.081351280212308}</string> + <string>{-40, 1.5446373167492311}</string> + </array> + <key>ID</key> + <integer>169333</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{344.9995533451783, 301.1612218744512}</string> + <string>{394.50000127156665, 299.00000000000045}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169332</integer> + <key>Info</key> + <integer>3</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{8.5833282470703125, -10.244596987647753}</string> + <string>{0, 0}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169051</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169062</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{272.41667175292969, 509.40064951817902}</string> + <string>{285.5, 503.81699882234847}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>Pattern</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169208</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169042</integer> + <key>Info</key> + <integer>4</integer> + </dict> + <key>ID</key> + <integer>169061</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 300.27229985501288}</string> + <string>{238.34892458824362, 260.57913893040109}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>0</string> + <key>Legacy</key> + <true/> + <key>Pattern</key> + <integer>2</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169034</integer> + <key>Position</key> + <real>0.51973581314086914</real> + </dict> + </dict> + <dict> + <key>Class</key> + <string>Group</string> + <key>Graphics</key> + <array> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 214.61452811107179}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169054</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.756045</string> + <key>g</key> + <string>0.75004</string> + <key>r</key> + <string>0.994455</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 exception}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 130.51770718892479}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169055</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 internal process}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 91.940789540609359}, {105.66666412353516, 33.089282989501953}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169056</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 external process (middleware, tween)}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 158.54998334248924}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169057</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 186.58225949605369}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169058</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 callback}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{419.66662216186666, 63.908513387045019}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169059</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 event}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{406.9999504089372, 42.910746256511771}, {132.66667175292969, 207.81692504882812}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169060</integer> + <key>Magnets</key> + <array> + <string>{1, 0.5}</string> + <string>{1, -0.5}</string> + <string>{-1, 0.5}</string> + <string>{-1, -0.5}</string> + <string>{0.5, 1}</string> + <string>{-0.5, 1}</string> + <string>{0.5, -1}</string> + <string>{-0.5, -1}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>CornerRadius</key> + <real>5</real> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Align</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 + +\f0\b\fs20 \cf0 Legend}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>TextPlacement</key> + <integer>0</integer> + </dict> + </array> + <key>ID</key> + <integer>169053</integer> + <key>Layer</key> + <integer>2</integer> + </dict> + <dict> + <key>Bounds</key> + <string>{{233.5000012715667, 20.000000000000934}, {116, 14}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>FitText</key> + <string>YES</string> + <key>Flow</key> + <string>Resize</string> + <key>FontInfo</key> + <dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>ID</key> + <integer>169052</integer> + <key>Layer</key> + <integer>2</integer> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + <key>stroke</key> + <dict> + <key>Draws</key> + <string>NO</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Pad</key> + <integer>0</integer> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\b\fs24 \cf0 <%Canvas%>}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + <key>Wrap</key> + <string>NO</string> + </dict> + <dict> + <key>Bounds</key> + <string>{{285.5, 492.544677734375}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169051</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 BeforeRender}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83337529500417, 335.17855853126167}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169340</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 HTTPForbidden}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83337529500417, 312.54463200342093}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169336</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 PredicateMismatch}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169081</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169048</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000001850648417, 128.2276785555359}</string> + <string>{82.999949137370791, 166.28868579864502}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169040</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169040</integer> + </dict> + <key>ID</key> + <integer>169047</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 67.727678571434836}</string> + <string>{83.000002543132396, 105.18303707668386}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169039</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 671.51189931233432}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169046</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 629.51189931233432}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169045</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 finished callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{239, 572.5118993123325}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169044</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.327428</string> + <key>g</key> + <string>0.81823</string> + <key>r</key> + <string>0.995566</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 response callbacks}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 548.10604731241608}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169043</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween egress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336130777977, 249.18303989230026}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169042</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999449</string> + <key>g</key> + <string>0.743511</string> + <key>r</key> + <string>0.872276</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 ContextFound}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 240.18303707668389}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169041</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.815377</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>0.820561</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 traversal}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 105.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169040</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 tween ingress}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{30.166670481364818, 45.18303707668386}, {105.66666412353516, 22.544641494750977}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169039</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.999208</string> + <key>g</key> + <string>0.811343</string> + <key>r</key> + <string>0.644457</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 middleware ingress }</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.49995040893634, 472.78869058972316}, {105.66666412353516, 42.300594329833984}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169358</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + <string>{-0.49999999999999956, -0.5}</string> + <string>{-0.49999999999999956, 0.5}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.422927</string> + <key>g</key> + <string>1</string> + <key>r</key> + <string>1</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 notfound_view / forbidden_view / exception_view}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{420.49995040893634, 290.66666666666691}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169344</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>fill</key> + <dict> + <key>Color</key> + <dict> + <key>b</key> + <string>0.756045</string> + <key>g</key> + <string>0.75004</string> + <key>r</key> + <string>0.994455</string> + </dict> + </dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 exception}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Bounds</key> + <string>{{238.83336512247806, 289.91071033477789}, {105.66666412353516, 22.544642175946908}}</string> + <key>Class</key> + <string>ShapedGraphic</string> + <key>ID</key> + <integer>169332</integer> + <key>Layer</key> + <integer>2</integer> + <key>Magnets</key> + <array> + <string>{0, 1}</string> + <string>{0, -1}</string> + <string>{1, 0}</string> + <string>{-1, 0}</string> + </array> + <key>Shape</key> + <string>Rectangle</string> + <key>Style</key> + <dict> + <key>shadow</key> + <dict> + <key>Draws</key> + <string>NO</string> + <key>ShadowVector</key> + <string>{2, 2}</string> + </dict> + </dict> + <key>Text</key> + <dict> + <key>Text</key> + <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf400 +\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs20 \cf0 HTTPNotFound}</string> + <key>VerticalPad</key> + <integer>0</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169041</integer> + </dict> + <key>ID</key> + <integer>169035</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{82.999949137370791, 216.62202930450437}</string> + <string>{83.000002543132396, 240.18303707668389}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169082</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.05596923828125}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169075</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169034</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 263.22767855635425}</string> + <string>{82.999997456869679, 335}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169041</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840087890625}</string> + <string>{0, -8.9999999999999432}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169070</integer> + </dict> + <key>ID</key> + <integer>169033</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{82.999997456869679, 385.33334350585938}</string> + <string>{83.000002543132396, 452.41914073626253}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169076</integer> + <key>Info</key> + <integer>1</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9839935302734375}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169046</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169128</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{83.000002543132396, 571.15068879140836}</string> + <string>{83.000002543132396, 671.51189931233421}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169043</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 7.055999755859375}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169355</integer> + <key>Info</key> + <integer>2</integer> + </dict> + <key>ID</key> + <integer>169357</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{473.33328247070392, 313.2113088426139}</string> + <string>{473.33338419596407, 338.15028762817326}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169344</integer> + </dict> + </dict> + <dict> + <key>Class</key> + <string>LineGraphic</string> + <key>ControlPoints</key> + <array> + <string>{0, 6.9840011596679688}</string> + <string>{0, -9}</string> + </array> + <key>FontInfo</key> + <dict> + <key>Color</key> + <dict> + <key>w</key> + <string>0</string> + </dict> + <key>Font</key> + <string>Helvetica</string> + <key>Size</key> + <real>12</real> + </dict> + <key>Head</key> + <dict> + <key>ID</key> + <integer>169358</integer> + </dict> + <key>ID</key> + <integer>169362</integer> + <key>Layer</key> + <integer>2</integer> + <key>Points</key> + <array> + <string>{473.33338359264764, 388.98363112285512}</string> + <string>{473.33328247070392, 472.78869058972316}</string> + </array> + <key>Style</key> + <dict> + <key>stroke</key> + <dict> + <key>Bezier</key> + <true/> + <key>Color</key> + <dict> + <key>b</key> + <string>0.0980392</string> + <key>g</key> + <string>0.0980392</string> + <key>r</key> + <string>0.0980392</string> + </dict> + <key>HeadArrow</key> + <string>SharpArrow</string> + <key>Legacy</key> + <true/> + <key>LineType</key> + <integer>1</integer> + <key>TailArrow</key> + <string>0</string> + </dict> + </dict> + <key>Tail</key> + <dict> + <key>ID</key> + <integer>169356</integer> + </dict> + </dict> + </array> + <key>GridInfo</key> + <dict/> + <key>GuidesLocked</key> + <string>NO</string> + <key>GuidesVisible</key> + <string>YES</string> + <key>HPages</key> + <integer>1</integer> + <key>ImageCounter</key> + <integer>3</integer> + <key>KeepToScale</key> + <false/> + <key>Layers</key> + <array> + <dict> + <key>Lock</key> + <string>NO</string> + <key>Name</key> + <string>no exceptions</string> + <key>Print</key> + <string>YES</string> + <key>View</key> + <string>YES</string> + </dict> + <dict> + <key>Lock</key> + <string>NO</string> + <key>Name</key> + <string>exceptions only</string> + <key>Print</key> + <string>YES</string> + <key>View</key> + <string>NO</string> + </dict> + <dict> + <key>Lock</key> + <string>NO</string> + <key>Name</key> + <string>all</string> + <key>Print</key> + <string>YES</string> + <key>View</key> + <string>NO</string> + </dict> + </array> + <key>LayoutInfo</key> + <dict> + <key>Animate</key> + <string>NO</string> + <key>circoMinDist</key> + <real>18</real> + <key>circoSeparation</key> + <real>0.0</real> + <key>layoutEngine</key> + <string>dot</string> + <key>neatoSeparation</key> + <real>0.0</real> + <key>twopiSeparation</key> + <real>0.0</real> + </dict> + <key>LinksVisible</key> + <string>NO</string> + <key>MagnetsVisible</key> + <string>NO</string> + <key>MasterSheets</key> + <array/> + <key>ModificationDate</key> + <string>2014-11-23 07:19:11 +0000</string> + <key>Modifier</key> + <string>Steve Piercy</string> + <key>NotesVisible</key> + <string>NO</string> + <key>Orientation</key> + <integer>2</integer> + <key>OriginVisible</key> + <string>NO</string> + <key>PageBreaks</key> + <string>YES</string> + <key>PrintInfo</key> + <dict> + <key>NSBottomMargin</key> + <array> + <string>float</string> + <string>41</string> + </array> + <key>NSHorizonalPagination</key> + <array> + <string>coded</string> + <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string> + </array> + <key>NSLeftMargin</key> + <array> + <string>float</string> + <string>18</string> + </array> + <key>NSPaperSize</key> + <array> + <string>size</string> + <string>{612, 792}</string> + </array> + <key>NSPrintReverseOrientation</key> + <array> + <string>int</string> + <string>0</string> + </array> + <key>NSRightMargin</key> + <array> + <string>float</string> + <string>18</string> + </array> + <key>NSTopMargin</key> + <array> + <string>float</string> + <string>18</string> + </array> + </dict> + <key>PrintOnePage</key> + <false/> + <key>ReadOnly</key> + <string>NO</string> + <key>RowAlign</key> + <integer>1</integer> + <key>RowSpacing</key> + <real>36</real> + <key>SheetTitle</key> + <string>Request Processing</string> + <key>SmartAlignmentGuidesActive</key> + <string>YES</string> + <key>SmartDistanceGuidesActive</key> + <string>YES</string> + <key>UniqueID</key> + <integer>1</integer> + <key>UseEntirePage</key> + <false/> + <key>VPages</key> + <integer>1</integer> + <key>WindowInfo</key> + <dict> + <key>CurrentSheet</key> + <integer>0</integer> + <key>ExpandedCanvases</key> + <array> + <dict> + <key>name</key> + <string>Request Processing</string> + </dict> + </array> + <key>Frame</key> + <string>{{35, 93}, {1394, 1325}}</string> + <key>ListView</key> + <true/> + <key>OutlineWidth</key> + <integer>178</integer> + <key>RightSidebar</key> + <true/> + <key>ShowRuler</key> + <true/> + <key>Sidebar</key> + <true/> + <key>SidebarWidth</key> + <integer>163</integer> + <key>VisibleRegion</key> + <string>{{-231, -226}, {1037, 1186}}</string> + <key>Zoom</key> + <real>1</real> + <key>ZoomValues</key> + <array> + <array> + <string>Request Processing</string> + <real>1</real> + <real>2</real> + </array> + </array> + </dict> +</dict> +</plist> diff --git a/docs/_static/pyramid_request_processing.png b/docs/_static/pyramid_request_processing.png Binary files differnew file mode 100644 index 000000000..2fbb1e164 --- /dev/null +++ b/docs/_static/pyramid_request_processing.png diff --git a/docs/_static/pyramid_request_processing.svg b/docs/_static/pyramid_request_processing.svg new file mode 100644 index 000000000..21bbcb532 --- /dev/null +++ b/docs/_static/pyramid_request_processing.svg @@ -0,0 +1,3 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="91 11 424 533" width="424pt" height="533pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2014-11-23 07:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker" viewBox="-4 -4 10 8" markerWidth="10" markerHeight="8" color="#191919"><g><path d="M 5 0 L -3 -3 L 0 0 L 0 0 L -3 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Request Processing</title><rect fill="white" width="576" height="733"/><g><title>no exceptions</title><path d="M 155 444.75674 C 155 450.64061 155 486.2592 155 502.71617" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99999 322.33334 C 154.99999 327.72413 155 337.74646 155 346.1775" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99999 245.22768 C 154.99999 250.5417 154.99999 257.93189 154.99999 265.10145" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 154.99995 198.62203 C 154.99995 203.74682 154.99998 209.1909 154.99999 215.28222" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="102.16667" y="45.183037" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="45.183037" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 50.455358)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="4.7596016" y="10" textLength="88.92578">middleware ingress </tspan></text><rect x="102.16667" y="96.183037" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="96.183037" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 101.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.983723" y="10" textLength="61.69922">tween ingress</tspan></text><rect x="102.16667" y="222.18304" width="105.666664" height="22.544641" fill="#d2ffd0"/><rect x="102.16667" y="222.18304" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 227.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="28.660969" y="10" textLength="38.344727">traversal</tspan></text><rect x="238.83336" y="247.18304" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="238.83336" y="247.18304" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 252.45536)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.424641" y="10" textLength="62.817383">ContextFound</tspan></text><rect x="102.16667" y="422.2121" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="422.2121" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 427.48442)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="18.094563" y="10" textLength="59.47754">tween egress</tspan></text><rect x="239" y="445.2359" width="105.666664" height="22.544641" fill="#fed153"/><rect x="239" y="445.2359" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244 450.50821)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="5.3113594" y="10" textLength="85.043945">response callbacks</tspan></text><rect x="239" y="497.2359" width="105.666664" height="22.544641" fill="#fed153"/><rect x="239" y="497.2359" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244 502.5082)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="8.6463203" y="10" textLength="5">fi</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="13.64632" y="10" textLength="73.374023">nished callbacks</tspan></text><rect x="102.16667" y="509.61795" width="105.666664" height="22.544641" fill="#a4cfff"/><rect x="102.16667" y="509.61795" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 514.89027)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="5.8704414" y="10" textLength="83.92578">middleware egress</tspan></text><path d="M 155 67.72768 C 155 73.048893 155 81.55558 155 89.2853" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 155 119.22768 C 155 124.62026 154.99997 133.48763 154.99996 141.38632" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="375.5" y="391" width="105.666664" height="22.544642" fill="#dfbeff"/><rect x="375.5" y="391" width="105.666664" height="22.544642" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(380.5 396.27232)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="16.702961" y="10" textLength="62.260742">BeforeRender</tspan></text><text transform="translate(233.5 20)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".31445312" y="11" textLength="115.371094">Request Processing</tspan></text><path d="M 375.99995 42.910746 L 498.66662 42.910746 C 501.42805 42.910746 503.66662 45.149323 503.66662 47.910746 L 503.66662 222 C 503.66662 224.76142 501.42805 227 498.66662 227 L 375.99995 227 C 373.23853 227 370.99995 224.76142 370.99995 222 L 370.99995 47.910746 C 370.99995 45.149323 373.23853 42.910746 375.99995 42.910746 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(375.99995 42.910746)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="bold" x="0" y="10" textLength="35.55664">Legend</tspan></text><rect x="383.66662" y="63.908513" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="383.66662" y="63.908513" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 69.180834)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="35.601887" y="10" textLength="24.46289">event</tspan></text><rect x="383.66662" y="186.58226" width="105.666664" height="22.544641" fill="#fed153"/><rect x="383.66662" y="186.58226" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 191.85458)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="29.769367" y="10" textLength="36.12793">callback</tspan></text><rect x="383.66662" y="158.54998" width="105.666664" height="22.544641" fill="#ffff6c"/><rect x="383.66662" y="158.54998" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 163.8223)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.83089" y="10" textLength="20.004883">view</tspan></text><rect x="383.66662" y="91.94079" width="105.666664" height="33.089283" fill="#a4cfff"/><rect x="383.66662" y="91.94079" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 96.48543)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="11.148762" y="10" textLength="76.14746">external process </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="2.8162422" y="22" textLength="90.03418">(middleware, tween)</tspan></text><rect x="383.66662" y="130.51771" width="105.666664" height="22.544641" fill="#d2ffd0"/><rect x="383.66662" y="130.51771" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(388.66662 135.79003)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="12.537922" y="10" textLength="70.59082">internal process</tspan></text><line x1="154.99999" y1="258.44082" x2="238.83336" y2="258.45536" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="102.16667" y="353.07515" width="105.666664" height="33.089283" fill="#ffff6c"/><rect x="102.16667" y="353.07515" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16667 363.61979)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.205402" y="10" textLength="57.25586">view pipeline</tspan></text><path d="M 155 386.66443 C 155 392.17252 155 405.5052 155 415.30935" marker-end="url(#SharpArrow_Marker)" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="239.33336" y1="285.57838" x2="207.66667" y2="353.07515" stroke="#c1c1c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/><line x1="238.75001" y1="430.80676" x2="207.66667" y2="385.656" stroke="#c1c1c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,3"/><rect x="102.16666" y="305.0893" width="105.666664" height="17.244049" fill="#d2ffd0"/><rect x="102.16666" y="305.0893" width="105.666664" height="17.244049" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16666 307.71132)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="24.764484" y="10" textLength="46.137695">predicates</tspan></text><rect x="102.16666" y="272" width="105.666664" height="33.089294" fill="#d2ffd0"/><rect x="102.16666" y="272" width="105.666664" height="33.089294" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.16666 282.54465)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="21.707844" y="10" textLength="52.250977">view lookup</tspan></text><rect x="102.166606" y="181.37798" width="105.666695" height="17.244049" fill="#d2ffd0"/><rect x="102.166606" y="181.37798" width="105.666695" height="17.244049" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.166606 184)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="11.978855" y="10" textLength="71.708984">route predicates</tspan></text><rect x="102.166606" y="148.28869" width="105.666695" height="33.089294" fill="#d2ffd0"/><rect x="102.166606" y="148.28869" width="105.666695" height="33.089294" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(107.166606 158.83333)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="18.001804" y="10" textLength="20.004883">URL</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.640476" y="10" textLength="40.024414"> dispatch</tspan></text><rect x="239.8334" y="117.3192" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="239.8334" y="117.3192" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(244.8334 122.59152)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.207844" y="10" textLength="57.250977">NewRequest</tspan></text><line x1="154.99999" y1="128.68025" x2="239.8334" y2="128.59152" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="238.83336" y="471.2262" width="105.666664" height="22.544641" fill="#dfbeff"/><rect x="238.83336" y="471.2262" width="105.666664" height="22.544641" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 476.49852)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="15.316242" y="10" textLength="65.03418">NewResponse</tspan></text><line x1="155" y1="470.25295" x2="238.33861" y2="482.42625" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><rect x="238.75001" y="322.26348" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="322.26348" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 325.5915)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="1.9812813" y="10" textLength="91.7041">view mapper ingress</tspan></text><rect x="238.75001" y="341.36561" width="105.666664" height="33.089283" fill="#ffff6c"/><rect x="238.75001" y="341.36561" width="105.666664" height="33.089283" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 351.91025)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.83089" y="10" textLength="20.004883">view</tspan></text><rect x="238.75001" y="374.901" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="374.901" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 378.22901)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="3.0921211" y="10" textLength="89.48242">view mapper egress</tspan></text><rect x="238.75001" y="393.55704" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="393.55704" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 396.88507)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="8.9173164" y="10" textLength="77.83203">response adapter</tspan></text><rect x="238.75001" y="303.65604" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="303.65604" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 306.98407)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="6.702961" y="10" textLength="82.26074">decorators ingress</tspan></text><rect x="238.75001" y="412.1507" width="105.666664" height="18.656048" fill="#ffffa3"/><rect x="238.75001" y="412.1507" width="105.666664" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.75001 415.47873)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="7.813801" y="10" textLength="80.039062">decorators egress</tspan></text><rect x="238.83336" y="285" width="105.66669" height="18.656048" fill="#ffffa3"/><rect x="238.83336" y="285" width="105.66669" height="18.656048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(243.83336 288.32802)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="19.202972" y="10" textLength="57.260742">authorization</tspan></text><line x1="155" y1="482.12575" x2="238.52297" y2="508.3584" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><line x1="155" y1="459.27668" x2="238.50027" y2="456.52468" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/><line x1="344.41667" y1="402.88507" x2="375.5" y2="402.27232" stroke="#191919" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="1,4"/></g></g></svg> diff --git a/docs/api/registry.rst b/docs/api/registry.rst index bab3e26ba..57a80b3f5 100644 --- a/docs/api/registry.rst +++ b/docs/api/registry.rst @@ -14,6 +14,18 @@ accessed as ``request.registry.settings`` or ``config.registry.settings`` in a typical Pyramid application. + .. attribute:: package_name + + .. versionadded:: 1.6 + + When a registry is set up (or created) by a :term:`Configurator`, this + attribute will be the shortcut for + :attr:`pyramid.config.Configurator.package_name`. + + This attribute is often accessed as ``request.registry.package_name`` or + ``config.registry.package_name`` or ``config.package_name`` + in a typical Pyramid application. + .. attribute:: introspector .. versionadded:: 1.3 diff --git a/docs/api/request.rst b/docs/api/request.rst index 77d80f6d6..dd68fa09c 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -167,37 +167,40 @@ .. versionadded:: 1.5 - A property which returns the userid of the currently authenticated user - or ``None`` if there is no :term:`authentication policy` in effect or - there is no currently authenticated user. This differs from - :attr:`~pyramid.request.Request.unauthenticated_userid`, because the - effective authentication policy will have ensured that a record - associated with the userid exists in persistent storage; if it has - not, this value will be ``None``. + A property which returns the :term:`userid` of the currently + authenticated user or ``None`` if there is no :term:`authentication + policy` in effect or there is no currently authenticated user. This + differs from :attr:`~pyramid.request.Request.unauthenticated_userid`, + because the effective authentication policy will have ensured that a + record associated with the :term:`userid` exists in persistent storage; + if it has not, this value will be ``None``. .. attribute:: unauthenticated_userid .. versionadded:: 1.5 A property which returns a value which represents the *claimed* (not - verified) user id of the credentials present in the request. ``None`` if - there is no :term:`authentication policy` in effect or there is no user - data associated with the current request. This differs from - :attr:`~pyramid.request.Request.authenticated_userid`, because the - effective authentication policy will not ensure that a record associated - with the userid exists in persistent storage. Even if the userid - does not exist in persistent storage, this value will be the value - of the userid *claimed* by the request data. + verified) :term:`userid` of the credentials present in the + request. ``None`` if there is no :term:`authentication policy` in effect + or there is no user data associated with the current request. This + differs from :attr:`~pyramid.request.Request.authenticated_userid`, + because the effective authentication policy will not ensure that a + record associated with the :term:`userid` exists in persistent storage. + Even if the :term:`userid` does not exist in persistent storage, this + value will be the value of the :term:`userid` *claimed* by the request + data. .. attribute:: effective_principals .. versionadded:: 1.5 A property which returns the list of 'effective' :term:`principal` - identifiers for this request. This will include the userid of the - currently authenticated user if a user is currently authenticated. If no - :term:`authentication policy` is in effect, this will return a sequence - containing only the :attr:`pyramid.security.Everyone` principal. + identifiers for this request. This list typically includes the + :term:`userid` of the currently authenticated user if a user is + currently authenticated, but this depends on the + :term:`authentication policy` in effect. If no :term:`authentication + policy` is in effect, this will return a sequence containing only the + :attr:`pyramid.security.Everyone` principal. .. method:: invoke_subrequest(request, use_tweens=False) diff --git a/docs/api/security.rst b/docs/api/security.rst index 814b68e5a..88086dbbf 100644 --- a/docs/api/security.rst +++ b/docs/api/security.rst @@ -16,7 +16,7 @@ Authentication API Functions .. autofunction:: forget -.. autofunction:: remember +.. autofunction:: remember(request, userid, **kwargs) Authorization API Functions --------------------------- diff --git a/docs/glossary.rst b/docs/glossary.rst index ef7e9a9ae..01300a0be 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -286,13 +286,22 @@ Glossary :term:`authorization policy`. principal - A *principal* is a string or unicode object representing a userid - or a group id. It is provided by an :term:`authentication - policy`. For example, if a user had the user id "bob", and Bob - was part of two groups named "group foo" and "group bar", the - request might have information attached to it that would - indicate that Bob was represented by three principals: "bob", - "group foo" and "group bar". + A *principal* is a string or unicode object representing an + entity, typically a user or group. Principals are provided by an + :term:`authentication policy`. For example, if a user had the + :term:`userid` `"bob"`, and was part of two groups named `"group foo"` + and "group bar", the request might have information attached to + it that would indicate that Bob was represented by three + principals: `"bob"`, `"group foo"` and `"group bar"`. + + userid + A *userid* is a string or unicode object used to identify and + authenticate a real-world user (or client). A userid is + supplied to an :term:`authentication policy` in order to discover + the user's :term:`principals <principal>`. The default behavior + of the authentication policies :app:`Pyramid` provides is to + return the user's userid as a principal, but this is not strictly + necessary in custom policies that define their principals differently. authorization policy An authorization policy in :app:`Pyramid` terms is a bit of diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 64dcab1d5..8c60407e5 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -15,3 +15,40 @@ class ViewTests(unittest.TestCase): request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['project'], 'MyProject') + +class ViewIntegrationTests(unittest.TestCase): + def setUp(self): + """ This sets up the application registry with the + registrations your application declares in its ``includeme`` + function. + """ + self.config = testing.setUp() + self.config.include('myproject') + + def tearDown(self): + """ Clear out the application registry """ + testing.tearDown() + + def test_my_view(self): + from myproject.views import my_view + request = testing.DummyRequest() + result = my_view(request) + self.assertEqual(result.status, '200 OK') + body = result.app_iter[0] + self.assertTrue('Welcome to' in body) + self.assertEqual(len(result.headerlist), 2) + self.assertEqual(result.headerlist[0], + ('Content-Type', 'text/html; charset=UTF-8')) + self.assertEqual(result.headerlist[1], ('Content-Length', + str(len(body)))) + +class FunctionalTests(unittest.TestCase): + def setUp(self): + from myproject import main + app = main({}) + from webtest import TestApp + self.testapp = TestApp(app) + + def test_root(self): + res = self.testapp.get('/', status=200) + self.assertTrue('Pyramid' in res.body) diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py index 8c019af51..9f34540a7 100644 --- a/docs/narr/MyProject/setup.py +++ b/docs/narr/MyProject/setup.py @@ -1,30 +1,42 @@ -import os +"""Setup for the MyProject package. +""" +import os from setuptools import setup, find_packages -here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.txt')) as f: - README = f.read() -with open(os.path.join(here, 'CHANGES.txt')) as f: - CHANGES = f.read() -requires = [ +HERE = os.path.abspath(os.path.dirname(__file__)) + + +with open(os.path.join(HERE, 'README.txt')) as fp: + README = fp.read() + + +with open(os.path.join(HERE, 'CHANGES.txt')) as fp: + CHANGES = fp.read() + + +REQUIRES = [ 'pyramid', 'pyramid_chameleon', 'pyramid_debugtoolbar', 'waitress', ] +TESTS_REQUIRE = [ + 'webtest' + ] + setup(name='MyProject', version='0.0', description='MyProject', long_description=README + '\n\n' + CHANGES, classifiers=[ - "Programming Language :: Python", - "Framework :: Pyramid", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], author='', author_email='', url='', @@ -32,11 +44,10 @@ setup(name='MyProject', packages=find_packages(), include_package_data=True, zip_safe=False, - install_requires=requires, - tests_require=requires, - test_suite="myproject", + install_requires=REQUIRES, + tests_require=TESTS_REQUIRE, + test_suite='myproject', entry_points="""\ [paste.app_factory] main = myproject:main - """, - ) + """) diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index 95863848b..fc908c2b4 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -276,15 +276,62 @@ to put static media on a separate webserver during production (if the ``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is a URL), while keeping static media package-internal and served by the development webserver during development (if the ``name`` argument to -:meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). To -create such a circumstance, we suggest using the -:attr:`pyramid.registry.Registry.settings` API in conjunction with a setting -in the application ``.ini`` file named ``media_location``. Then set the -value of ``media_location`` to either a prefix or a URL depending on whether -the application is being run in development or in production (use a different -``.ini`` file for production than you do for development). This is just a -suggestion for a pattern; any setting name other than ``media_location`` -could be used. +:meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). + +For example, we may define a :ref:`custom setting <adding_a_custom_setting>` +named ``media_location`` which we can set to an external URL in production +when our assets are hosted on a CDN. + +.. code-block:: python + :linenos: + + media_location = settings.get('media_location', 'static') + + config = Configurator(settings=settings) + config.add_static_view(path='myapp:static', name=media_location) + +Now we can optionally define the setting in our ini file: + +.. code-block:: ini + :linenos: + + # production.ini + [app:main] + use = egg:myapp#main + + media_location = http://static.example.com/ + +It is also possible to serve assets that live outside of the source by +referring to an absolute path on the filesystem. There are two ways to +accomplish this. + +First, :meth:`~pyramid.config.Configurator.add_static_view` +supports taking an absolute path directly instead of an asset spec. This works +as expected, looking in the file or folder of files and serving them up at +some URL within your application or externally. Unfortunately, this technique +has a drawback that it is not possible to use the +:meth:`~pyramid.request.Request.static_url` method to generate URLs, since it +works based on an asset spec. + +The second approach, available in Pyramid 1.6+, uses the asset overriding +APIs described in the :ref:`overriding_assets_section` section. It is then +possible to configure a "dummy" package which then serves its file or folder +from an absolute path. + +.. code-block:: python + + config.add_static_view(path='myapp:static_images', name='static') + config.override_asset(to_override='myapp:static_images/', + override_with='/abs/path/to/images/') + +From this configuration it is now possible to use +:meth:`~pyramid.request.Request.static_url` to generate URLs to the data +in the folder by doing something like +``request.static_url('myapp:static_images/foo.png')``. While it is not +necessary that the ``static_images`` file or folder actually exist in the +``myapp`` package, it is important that the ``myapp`` portion points to a +valid package. If the folder does exist then the overriden folder is given +priority if the file's name exists in both locations. .. index:: single: Cache Busting @@ -701,3 +748,6 @@ files. Any software which uses the :func:`pkg_resources.get_resource_string` APIs will obtain an overridden file when an override is used. +As of Pyramid 1.6, it is also possible to override an asset by supplying an +absolute path to a file or directory. This may be useful if the assets are +not distributed as part of a Python package. diff --git a/docs/narr/router.rst b/docs/narr/router.rst index ac3deefdc..e82b66801 100644 --- a/docs/narr/router.rst +++ b/docs/narr/router.rst @@ -9,6 +9,9 @@ Request Processing ================== +.. image:: ../_static/pyramid_request_processing.svg + :alt: Request Processing + Once a :app:`Pyramid` application is up and running, it is ready to accept requests and return responses. What happens from the time a :term:`WSGI` request enters a :app:`Pyramid` application through to the point that diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 8db23a33b..2dc0c76af 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -6,13 +6,28 @@ Security ======== -:app:`Pyramid` provides an optional declarative authorization system -that can prevent a :term:`view` from being invoked based on an +:app:`Pyramid` provides an optional, declarative, security system. +Security in :app:`Pyramid` is separated into authentication and +authorization. The two systems communicate via :term:`principal` +identifiers. Authentication is merely the mechanism by which credentials +provided in the :term:`request` are resolved to one or more +:term:`principal` identifiers. These identifiers represent the users and +groups that are in effect during the request. Authorization then determines +access based on the :term:`principal` identifiers, the requested +:term:`permission`, and a :term:`context`. + +The :app:`Pyramid` authorization system +can prevent a :term:`view` from being invoked based on an :term:`authorization policy`. Before a view is invoked, the authorization system can use the credentials in the :term:`request` along with the :term:`context` resource to determine if access will be allowed. Here's how it works at a high level: +- A user may or may not have previously visited the application and + supplied authentication credentials, including a :term:`userid`. If + so, the application may have called + :func:`pyramid.security.remember` to remember these. + - A :term:`request` is generated when a user visits the application. - Based on the request, a :term:`context` resource is located through @@ -25,7 +40,9 @@ allowed. Here's how it works at a high level: context as well as other attributes of the request. - If an :term:`authentication policy` is in effect, it is passed the - request; it returns some number of :term:`principal` identifiers. + request. It will return some number of :term:`principal` identifiers. + To do this, the policy would need to determine the authenticated + :term:`userid` present in the request. - If an :term:`authorization policy` is in effect and the :term:`view configuration` associated with the view callable that was found has @@ -41,15 +58,6 @@ allowed. Here's how it works at a high level: - If the authorization policy denies access, the view callable is not invoked; instead the :term:`forbidden view` is invoked. -Security in :app:`Pyramid`, unlike many systems, cleanly and explicitly -separates authentication and authorization. Authentication is merely the -mechanism by which credentials provided in the :term:`request` are -resolved to one or more :term:`principal` identifiers. These identifiers -represent the users and groups in effect during the request. -Authorization then determines access based on the :term:`principal` -identifiers, the :term:`view callable` being invoked, and the -:term:`context` resource. - Authorization is enabled by modifying your application to include an :term:`authentication policy` and :term:`authorization policy`. :app:`Pyramid` comes with a variety of implementations of these @@ -104,7 +112,8 @@ For example: The above configuration enables a policy which compares the value of an "auth ticket" cookie passed in the request's environment which contains a reference -to a single :term:`principal` against the principals present in any +to a single :term:`userid` and matches that userid's +:term:`principals <principal>` against the principals present in any :term:`ACL` found in the resource tree when attempting to call some :term:`view`. @@ -595,36 +604,53 @@ that implements the following interface: """ An object representing a Pyramid authentication policy. """ def authenticated_userid(self, request): - """ Return the authenticated userid or ``None`` if no - authenticated userid can be found. This method of the policy - should ensure that a record exists in whatever persistent store is - used related to the user (the user should not have been deleted); - if a record associated with the current id does not exist in a - persistent store, it should return ``None``.""" + """ Return the authenticated :term:`userid` or ``None`` if + no authenticated userid can be found. This method of the + policy should ensure that a record exists in whatever + persistent store is used related to the user (the user + should not have been deleted); if a record associated with + the current id does not exist in a persistent store, it + should return ``None``. + + """ def unauthenticated_userid(self, request): - """ Return the *unauthenticated* userid. This method performs the - same duty as ``authenticated_userid`` but is permitted to return the - userid based only on data present in the request; it needn't (and - shouldn't) check any persistent store to ensure that the user record - related to the request userid exists.""" + """ Return the *unauthenticated* userid. This method + performs the same duty as ``authenticated_userid`` but is + permitted to return the userid based only on data present + in the request; it needn't (and shouldn't) check any + persistent store to ensure that the user record related to + the request userid exists. + + This method is intended primarily a helper to assist the + ``authenticated_userid`` method in pulling credentials out + of the request data, abstracting away the specific headers, + query strings, etc that are used to authenticate the request. + + """ def effective_principals(self, request): """ Return a sequence representing the effective principals - including the userid and any groups belonged to by the current - user, including 'system' groups such as - ``pyramid.security.Everyone`` and - ``pyramid.security.Authenticated``. """ + typically including the :term:`userid` and any groups belonged + to by the current user, always including 'system' groups such + as ``pyramid.security.Everyone`` and + ``pyramid.security.Authenticated``. - def remember(self, request, principal, **kw): + """ + + def remember(self, request, userid, **kw): """ Return a set of headers suitable for 'remembering' the - principal named ``principal`` when set in a response. An - individual authentication policy and its consumers can decide - on the composition and meaning of **kw. """ - + :term:`userid` named ``userid`` when set in a response. An + individual authentication policy and its consumers can + decide on the composition and meaning of **kw. + + """ + def forget(self, request): """ Return a set of headers suitable for 'forgetting' the - current user on subsequent requests. """ + current user on subsequent requests. + + """ After you do so, you can pass an instance of such a class into the :class:`~pyramid.config.Configurator.set_authentication_policy` method diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst index e001ad81c..ecda57489 100644 --- a/docs/narr/testing.rst +++ b/docs/narr/testing.rst @@ -128,8 +128,9 @@ functions accepts various arguments that influence the environment of the test. See the :ref:`testing_module` API for information about the extra arguments supported by these functions. -If you also want to make :func:`~pyramid.threadlocal.get_current_request` return something -other than ``None`` during the course of a single test, you can pass a +If you also want to make :func:`~pyramid.threadlocal.get_current_request` +return something other than ``None`` during the course of a single test, you +can pass a :term:`request` object into the :func:`pyramid.testing.setUp` within the ``setUp`` method of your test: @@ -333,66 +334,49 @@ Creating Integration Tests -------------------------- In :app:`Pyramid`, a *unit test* typically relies on "mock" or "dummy" -implementations to give the code under test only enough context to run. +implementations to give the code under test enough context to run. "Integration testing" implies another sort of testing. In the context of a -:app:`Pyramid` integration test, the test logic tests the functionality of -some code *and* its integration with the rest of the :app:`Pyramid` +:app:`Pyramid` integration test, the test logic exercises the functionality of +the code under test *and* its integration with the rest of the :app:`Pyramid` framework. -In :app:`Pyramid` applications that are plugins to Pyramid, you can create an -integration test by including its ``includeme`` function via -:meth:`pyramid.config.Configurator.include` in the test's setup code. This -causes the entire :app:`Pyramid` environment to be set up and torn down as if -your application was running "for real". This is a heavy-hammer way of -making sure that your tests have enough context to run properly, and it tests -your code's integration with the rest of :app:`Pyramid`. +Creating an integration test for a :app:`Pyramid` application usually means +invoking the application's ``includeme`` function via +:meth:`pyramid.config.Configurator.include` within the test's setup code. This +causes the entire :app:`Pyramid` environment to be set up, simulating what +happens when your application is run "for real". This is a heavy-hammer way of +making sure that your tests have enough context to run properly, and tests your +code's integration with the rest of :app:`Pyramid`. -Let's demonstrate this by showing an integration test for a view. The below -test assumes that your application's package name is ``myapp``, and that -there is a ``views`` module in the app with a function with the name -``my_view`` in it that returns the response 'Welcome to this application' -after accessing some values that require a fully set up environment. +.. seealso:: -.. code-block:: python - :linenos: + See also :ref:`including_configuration` - import unittest +Let's demonstrate this by showing an integration test for a view. - from pyramid import testing +Given the following view definition, which assumes that your application's +:term:`package` name is ``myproject``, and within that :term:`package` there +exists a module ``views``, which in turn contains a :term:`view` function named +``my_view``: - class ViewIntegrationTests(unittest.TestCase): - def setUp(self): - """ This sets up the application registry with the - registrations your application declares in its ``includeme`` - function. - """ - import myapp - self.config = testing.setUp() - self.config.include('myapp') + .. literalinclude:: MyProject/myproject/views.py + :linenos: + :lines: 1-6 + :language: python - def tearDown(self): - """ Clear out the application registry """ - testing.tearDown() +You'd then create a ``tests`` module within your ``myproject`` package, +containing the following test code: - def test_my_view(self): - from myapp.views import my_view - request = testing.DummyRequest() - result = my_view(request) - self.assertEqual(result.status, '200 OK') - body = result.app_iter[0] - self.assertTrue('Welcome to' in body) - self.assertEqual(len(result.headerlist), 2) - self.assertEqual(result.headerlist[0], - ('Content-Type', 'text/html; charset=UTF-8')) - self.assertEqual(result.headerlist[1], ('Content-Length', - str(len(body)))) - -Unless you cannot avoid it, you should prefer writing unit tests that use the -:class:`~pyramid.config.Configurator` API to set up the right "mock" -registrations rather than creating an integration test. Unit tests will run -faster (because they do less for each test) and the result of a unit test is -usually easier to make assertions about. + .. literalinclude:: MyProject/myproject/tests.py + :linenos: + :pyobject: ViewIntegrationTests + :language: python + +Writing unit tests that use the :class:`~pyramid.config.Configurator` API to +set up the right "mock" registrations is often preferred to creating +integration tests. Unit tests will run faster (because they do less for each +test) and are usually easier to reason about. .. index:: single: functional tests @@ -404,34 +388,40 @@ Creating Functional Tests Functional tests test your literal application. -The below test assumes that your application's package name is ``myapp``, and -that there is a view that returns an HTML body when the root URL is invoked. -It further assumes that you've added a ``tests_require`` dependency on the -``WebTest`` package within your ``setup.py`` file. :term:`WebTest` is a -functional testing package written by Ian Bicking. +In Pyramid, functional tests are typically written using the :term:`WebTest` +package, which provides APIs for invoking HTTP(S) requests to your application. -.. code-block:: python - :linenos: +Regardless of which testing :term:`package` you use, ensure to add a +``tests_require`` dependency on that package to to your application's +``setup.py`` file: - import unittest + .. literalinclude:: MyProject/setup.py + :linenos: + :emphasize-lines: 26-28,48 + :language: python - class FunctionalTests(unittest.TestCase): - def setUp(self): - from myapp import main - app = main({}) - from webtest import TestApp - self.testapp = TestApp(app) - - def test_root(self): - res = self.testapp.get('/', status=200) - self.assertTrue('Pyramid' in res.body) - -When this test is run, each test creates a "real" WSGI application using the -``main`` function in your ``myapp.__init__`` module and uses :term:`WebTest` -to wrap that WSGI application. It assigns the result to ``self.testapp``. -In the test named ``test_root``, we use the testapp's ``get`` method to -invoke the root URL. We then assert that the returned HTML has the string -``Pyramid`` in it. +Assuming your :term:`package` is named ``myproject``, which contains a +``views`` module, which in turn contains a :term:`view` function ``my_view`` +that returns a HTML body when the root URL is invoked: + + .. literalinclude:: MyProject/myproject/views.py + :linenos: + :language: python + +Then the following example functional test (shown below) demonstrates invoking +the :term:`view` shown above: + + .. literalinclude:: MyProject/myproject/tests.py + :linenos: + :pyobject: FunctionalTests + :language: python + +When this test is run, each test method creates a "real" :term:`WSGI` +application using the ``main`` function in your ``myproject.__init__`` module, +using :term:`WebTest` to wrap that WSGI application. It assigns the result to +``self.testapp``. In the test named ``test_root``. The ``TestApp``'s ``get`` +method is used to invoke the root URL. Finally, an assertion is made that the +returned HTML contains the text ``MyProject``. See the :term:`WebTest` documentation for further information about the methods available to a :class:`webtest.app.TestApp` instance. diff --git a/docs/tutorials/wiki/design.rst b/docs/tutorials/wiki/design.rst index eb785dd1c..28380bd66 100644 --- a/docs/tutorials/wiki/design.rst +++ b/docs/tutorials/wiki/design.rst @@ -53,10 +53,10 @@ Security We'll eventually be adding security to our application. The components we'll use to do this are below. -- USERS, a dictionary mapping usernames to their +- USERS, a dictionary mapping :term:`userids <userid>` to their corresponding passwords. -- GROUPS, a dictionary mapping usernames to a +- GROUPS, a dictionary mapping :term:`userids <userid>` to a list of groups to which they belong to. - ``groupfinder``, an *authorization callback* that looks up diff --git a/docs/tutorials/wiki2/design.rst b/docs/tutorials/wiki2/design.rst index df2c83398..ff7413668 100644 --- a/docs/tutorials/wiki2/design.rst +++ b/docs/tutorials/wiki2/design.rst @@ -53,7 +53,8 @@ Security We'll eventually be adding security to our application. The components we'll use to do this are below. -- USERS, a dictionary mapping users names to their corresponding passwords. +- USERS, a dictionary mapping users names (the user's :term:`userids + <userid>`) to their corresponding passwords. - GROUPS, a dictionary mapping user names to a list of groups they belong to. diff --git a/pyramid/authentication.py b/pyramid/authentication.py index b84981bbc..e0e241e52 100644 --- a/pyramid/authentication.py +++ b/pyramid/authentication.py @@ -3,7 +3,6 @@ from codecs import utf_8_decode from codecs import utf_8_encode import hashlib import base64 -import datetime import re import time as time_mod import warnings @@ -335,11 +334,11 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): effective_principals.extend(groups) return effective_principals - def remember(self, request, principal, **kw): - """ Store the ``principal`` as ``repoze.who.userid``. + def remember(self, request, userid, **kw): + """ Store the ``userid`` as ``repoze.who.userid``. The identity to authenticated to :mod:`repoze.who` - will contain the given principal as ``userid``, and + will contain the given userid as ``userid``, and provide all keyword arguments as additional identity keys. Useful keys could be ``max_age`` or ``userdata``. """ @@ -348,7 +347,7 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy): return [] environ = request.environ identity = kw - identity['repoze.who.userid'] = principal + identity['repoze.who.userid'] = userid return identifier.remember(environ, identity) def forget(self, request): @@ -404,7 +403,7 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy): """ The ``REMOTE_USER`` value found within the ``environ``.""" return request.environ.get(self.environ_key) - def remember(self, request, principal, **kw): + def remember(self, request, userid, **kw): """ A no-op. The ``REMOTE_USER`` does not provide a protocol for remembering the user. This will be application-specific and can be done somewhere else or in a subclass.""" @@ -652,7 +651,7 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): if result: return result['userid'] - def remember(self, request, principal, **kw): + def remember(self, request, userid, **kw): """ Accepts the following kw args: ``max_age=<int-seconds>, ``tokens=<sequence-of-ascii-strings>``. @@ -660,7 +659,7 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy): the response. """ - return self.cookie.remember(request, principal, **kw) + return self.cookie.remember(request, userid, **kw) def forget(self, request): """ A list of headers which will delete appropriate cookies.""" @@ -741,7 +740,7 @@ def parse_ticket(secret, ticket, ip, hashalg='md5'): If the ticket cannot be parsed, a ``BadTicket`` exception will be raised with an explanation. """ - ticket = ticket.strip('"') + ticket = native_(ticket).strip('"') digest_size = hashlib.new(hashalg).digest_size * 2 digest = ticket[:digest_size] try: @@ -929,7 +928,7 @@ class AuthTktCookieHelper(object): if reissue and not hasattr(request, '_authtkt_reissued'): if ( (now - timestamp) > self.reissue_time ): - # work around https://github.com/Pylons/pyramid/issues#issue/108 + # See https://github.com/Pylons/pyramid/issues#issue/108 tokens = list(filter(None, tokens)) headers = self.remember(request, userid, max_age=self.max_age, tokens=tokens) @@ -1061,13 +1060,13 @@ class SessionAuthenticationPolicy(CallbackAuthenticationPolicy): self.userid_key = prefix + 'userid' self.debug = debug - def remember(self, request, principal, **kw): - """ Store a principal in the session.""" - request.session[self.userid_key] = principal + def remember(self, request, userid, **kw): + """ Store a userid in the session.""" + request.session[self.userid_key] = userid return [] def forget(self, request): - """ Remove the stored principal from the session.""" + """ Remove the stored userid from the session.""" if self.userid_key in request.session: del request.session[self.userid_key] return [] @@ -1132,7 +1131,7 @@ class BasicAuthAuthenticationPolicy(CallbackAuthenticationPolicy): if credentials: return credentials[0] - def remember(self, request, principal, **kw): + def remember(self, request, userid, **kw): """ A no-op. Basic authentication does not provide a protocol for remembering the user. Credentials are sent on every request. diff --git a/pyramid/config/assets.py b/pyramid/config/assets.py index 0616e6cda..9da092f08 100644 --- a/pyramid/config/assets.py +++ b/pyramid/config/assets.py @@ -1,3 +1,4 @@ +import os import pkg_resources import sys @@ -79,7 +80,8 @@ class OverrideProvider(pkg_resources.DefaultProvider): return result return pkg_resources.DefaultProvider.resource_listdir( self, resource_name) - + + @implementer(IPackageOverrides) class PackageOverrides(object): # pkg_resources arg in kw args below for testing @@ -97,57 +99,61 @@ class PackageOverrides(object): # optional)... # A __loader__ attribute is basically metadata, and setuptools # uses it as such. - package.__loader__ = self + package.__loader__ = self # we call register_loader_type for every instantiation of this # class; that's OK, it's idempotent to do it more than once. pkg_resources.register_loader_type(self.__class__, OverrideProvider) self.overrides = [] self.overridden_package_name = package.__name__ - def insert(self, path, package, prefix): + def insert(self, path, source): if not path or path.endswith('/'): - override = DirectoryOverride(path, package, prefix) + override = DirectoryOverride(path, source) else: - override = FileOverride(path, package, prefix) + override = FileOverride(path, source) self.overrides.insert(0, override) return override - def search_path(self, resource_name): + def filtered_sources(self, resource_name): for override in self.overrides: o = override(resource_name) if o is not None: - package, name = o - yield package, name + yield o def get_filename(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_filename(package, rname) + for source, path in self.filtered_sources(resource_name): + result = source.get_filename(path) + if result is not None: + return result def get_stream(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_stream(package, rname) + for source, path in self.filtered_sources(resource_name): + result = source.get_stream(path) + if result is not None: + return result def get_string(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_string(package, rname) + for source, path in self.filtered_sources(resource_name): + result = source.get_string(path) + if result is not None: + return result def has_resource(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): + for source, path in self.filtered_sources(resource_name): + if source.exists(path): return True def isdir(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_isdir(package, rname) + for source, path in self.filtered_sources(resource_name): + result = source.isdir(path) + if result is not None: + return result def listdir(self, resource_name): - for package, rname in self.search_path(resource_name): - if pkg_resources.resource_exists(package, rname): - return pkg_resources.resource_listdir(package, rname) + for source, path in self.filtered_sources(resource_name): + result = source.listdir(path) + if result is not None: + return result @property def real_loader(self): @@ -174,72 +180,180 @@ class PackageOverrides(object): """ See IPEP302Loader. """ return self.real_loader.get_source(fullname) - + class DirectoryOverride: - def __init__(self, path, package, prefix): + def __init__(self, path, source): self.path = path - self.package = package - self.prefix = prefix self.pathlen = len(self.path) + self.source = source def __call__(self, resource_name): if resource_name.startswith(self.path): - name = '%s%s' % (self.prefix, resource_name[self.pathlen:]) - return self.package, name + new_path = resource_name[self.pathlen:] + return self.source, new_path class FileOverride: - def __init__(self, path, package, prefix): + def __init__(self, path, source): self.path = path - self.package = package - self.prefix = prefix + self.source = source def __call__(self, resource_name): if resource_name == self.path: - return self.package, self.prefix + return self.source, '' + + +class PackageAssetSource(object): + """ + An asset source relative to a package. + + If this asset source is a file, then we expect the ``prefix`` to point + to the new name of the file, and the incoming ``resource_name`` will be + the empty string, as returned by the ``FileOverride``. + + """ + def __init__(self, package, prefix): + self.package = package + self.prefix = prefix + + def get_path(self, resource_name): + return '%s%s' % (self.prefix, resource_name) + + def get_filename(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return pkg_resources.resource_filename(self.package, path) + + def get_stream(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return pkg_resources.resource_stream(self.package, path) + + def get_string(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return pkg_resources.resource_string(self.package, path) + + def exists(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return True + + def isdir(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return pkg_resources.resource_isdir(self.package, path) + + def listdir(self, resource_name): + path = self.get_path(resource_name) + if pkg_resources.resource_exists(self.package, path): + return pkg_resources.resource_listdir(self.package, path) + + +class FSAssetSource(object): + """ + An asset source relative to a path in the filesystem. + + """ + def __init__(self, prefix): + self.prefix = prefix + + def get_filename(self, resource_name): + if resource_name: + path = os.path.join(self.prefix, resource_name) + else: + path = self.prefix + + if os.path.exists(path): + return path + + def get_stream(self, resource_name): + path = self.get_filename(resource_name) + if path is not None: + return open(path, 'rb') + + def get_string(self, resource_name): + stream = self.get_stream(resource_name) + if stream is not None: + with stream: + return stream.read() + + def exists(self, resource_name): + path = self.get_filename(resource_name) + if path is not None: + return True + + def isdir(self, resource_name): + path = self.get_filename(resource_name) + if path is not None: + return os.path.isdir(path) + + def listdir(self, resource_name): + path = self.get_filename(resource_name) + if path is not None: + return os.listdir(path) class AssetsConfiguratorMixin(object): - def _override(self, package, path, override_package, override_prefix, + def _override(self, package, path, override_source, PackageOverrides=PackageOverrides): pkg_name = package.__name__ - override_pkg_name = override_package.__name__ override = self.registry.queryUtility(IPackageOverrides, name=pkg_name) if override is None: override = PackageOverrides(package) self.registry.registerUtility(override, IPackageOverrides, name=pkg_name) - override.insert(path, override_pkg_name, override_prefix) + override.insert(path, override_source) @action_method def override_asset(self, to_override, override_with, _override=None): """ Add a :app:`Pyramid` asset override to the current configuration state. - ``to_override`` is a :term:`asset specification` to the + ``to_override`` is an :term:`asset specification` to the asset being overridden. - ``override_with`` is a :term:`asset specification` to the - asset that is performing the override. + ``override_with`` is an :term:`asset specification` to the + asset that is performing the override. This may also be an absolute + path. See :ref:`assets_chapter` for more information about asset overrides.""" if to_override == override_with: - raise ConfigurationError('You cannot override an asset with itself') + raise ConfigurationError( + 'You cannot override an asset with itself') package = to_override path = '' if ':' in to_override: package, path = to_override.split(':', 1) - override_package = override_with - override_prefix = '' - if ':' in override_with: - override_package, override_prefix = override_with.split(':', 1) - # *_isdir = override is package or directory - overridden_isdir = path=='' or path.endswith('/') - override_isdir = override_prefix=='' or override_prefix.endswith('/') + overridden_isdir = path == '' or path.endswith('/') + + if os.path.isabs(override_with): + override_source = FSAssetSource(override_with) + if not os.path.exists(override_with): + raise ConfigurationError( + 'Cannot override asset with an absolute path that does ' + 'not exist') + override_isdir = os.path.isdir(override_with) + override_package = None + override_prefix = override_with + else: + override_package = override_with + override_prefix = '' + if ':' in override_with: + override_package, override_prefix = override_with.split(':', 1) + + __import__(override_package) + to_package = sys.modules[override_package] + override_source = PackageAssetSource(to_package, override_prefix) + + override_isdir = ( + override_prefix == '' or + override_with.endswith('/') + ) if overridden_isdir and (not override_isdir): raise ConfigurationError( @@ -255,10 +369,8 @@ class AssetsConfiguratorMixin(object): def register(): __import__(package) - __import__(override_package) from_package = sys.modules[package] - to_package = sys.modules[override_package] - override(from_package, path, to_package, override_prefix) + override(from_package, path, override_source) intr = self.introspectable( 'asset overrides', diff --git a/pyramid/config/views.py b/pyramid/config/views.py index e4171b0c5..c01b72e12 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -1186,10 +1186,6 @@ class ViewsConfiguratorMixin(object): predlist = self.get_predlist('view') def register(permission=permission, renderer=renderer): - # the discrim_func above is guaranteed to have been called already - order = view_intr['order'] - preds = view_intr['predicates'] - phash = view_intr['phash'] request_iface = IRequest if route_name is not None: request_iface = self.registry.queryUtility(IRouteRequest, @@ -1955,7 +1951,7 @@ class StaticURLInfo(object): sep = os.sep else: sep = '/' - if not spec.endswith(sep): + if not spec.endswith(sep) and not spec.endswith(':'): spec = spec + sep # we also make sure the name ends with a slash, purely as a diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index c5a70dbfd..b21c6b9cc 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -439,36 +439,55 @@ class IViewMapperFactory(Interface): class IAuthenticationPolicy(Interface): """ An object representing a Pyramid authentication policy. """ + def authenticated_userid(request): - """ Return the authenticated userid or ``None`` if no authenticated - userid can be found. This method of the policy should ensure that a - record exists in whatever persistent store is used related to the - user (the user should not have been deleted); if a record associated - with the current id does not exist in a persistent store, it should - return ``None``.""" + """ Return the authenticated :term:`userid` or ``None`` if + no authenticated userid can be found. This method of the + policy should ensure that a record exists in whatever + persistent store is used related to the user (the user + should not have been deleted); if a record associated with + the current id does not exist in a persistent store, it + should return ``None``. + + """ def unauthenticated_userid(request): - """ Return the *unauthenticated* userid. This method performs the - same duty as ``authenticated_userid`` but is permitted to return the - userid based only on data present in the request; it needn't (and - shouldn't) check any persistent store to ensure that the user record - related to the request userid exists.""" + """ Return the *unauthenticated* userid. This method + performs the same duty as ``authenticated_userid`` but is + permitted to return the userid based only on data present + in the request; it needn't (and shouldn't) check any + persistent store to ensure that the user record related to + the request userid exists. + + This method is intended primarily a helper to assist the + ``authenticated_userid`` method in pulling credentials out + of the request data, abstracting away the specific headers, + query strings, etc that are used to authenticate the request. + + """ def effective_principals(request): """ Return a sequence representing the effective principals - including the userid and any groups belonged to by the current - user, including 'system' groups such as Everyone and - Authenticated. """ + typically including the :term:`userid` and any groups belonged + to by the current user, always including 'system' groups such + as ``pyramid.security.Everyone`` and + ``pyramid.security.Authenticated``. - def remember(request, principal, **kw): + """ + + def remember(request, userid, **kw): """ Return a set of headers suitable for 'remembering' the - principal named ``principal`` when set in a response. An - individual authentication policy and its consumers can decide - on the composition and meaning of ``**kw.`` """ + :term:`userid` named ``userid`` when set in a response. An + individual authentication policy and its consumers can + decide on the composition and meaning of ``**kw``. + + """ def forget(request): """ Return a set of headers suitable for 'forgetting' the - current user on subsequent requests. """ + current user on subsequent requests. + + """ class IAuthorizationPolicy(Interface): """ An object representing a Pyramid authorization policy. """ diff --git a/pyramid/registry.py b/pyramid/registry.py index 606251a8d..8c05940b9 100644 --- a/pyramid/registry.py +++ b/pyramid/registry.py @@ -5,6 +5,7 @@ from zope.interface import implementer from zope.interface.registry import Components from pyramid.compat import text_ +from pyramid.decorator import reify from pyramid.interfaces import ( ISettings, @@ -42,6 +43,10 @@ class Registry(Components, dict): # defeat bool determination via dict.__len__ return True + @reify + def package_name(self): + return self.__name__ + def registerSubscriptionAdapter(self, *arg, **kw): result = Components.registerSubscriptionAdapter(self, *arg, **kw) self.has_listeners = True diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py index 5784026bb..d0c1aa13e 100644 --- a/pyramid/scripts/proutes.py +++ b/pyramid/scripts/proutes.py @@ -5,10 +5,15 @@ import textwrap from pyramid.paster import bootstrap from pyramid.scripts.common import parse_vars + +PAD = 3 + + def main(argv=sys.argv, quiet=False): command = PRoutesCommand(argv, quiet) return command.run() + class PRoutesCommand(object): description = """\ Print all URL dispatch routes used by a Pyramid application in the @@ -43,7 +48,7 @@ class PRoutesCommand(object): def out(self, msg): # pragma: no cover if not self.quiet: print(msg) - + def run(self, quiet=False): if not self.args: self.out('requires a config file argument') @@ -52,6 +57,8 @@ class PRoutesCommand(object): from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from zope.interface import Interface config_uri = self.args[0] @@ -59,13 +66,23 @@ class PRoutesCommand(object): registry = env['registry'] mapper = self._get_mapper(registry) if mapper is not None: + mapped_routes = [('Name', 'Pattern', 'View')] + + max_name = len('Name') + max_pattern = len('Pattern') + max_view = len('View') + routes = mapper.get_routes() - fmt = '%-15s %-30s %-25s' + if not routes: return 0 - self.out(fmt % ('Name', 'Pattern', 'View')) - self.out( - fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) + + mapped_routes.append(( + '-' * max_name, + '-' * max_pattern, + '-' * max_view, + )) + for route in routes: pattern = route.pattern if not pattern.startswith('/'): @@ -73,13 +90,50 @@ class PRoutesCommand(object): request_iface = registry.queryUtility(IRouteRequest, name=route.name) view_callable = None + if (request_iface is None) or (route.factory is not None): - self.out(fmt % (route.name, pattern, '<unknown>')) + view_callable = '<unknown>' else: view_callable = registry.adapters.lookup( (IViewClassifier, request_iface, Interface), IView, name='', default=None) - self.out(fmt % (route.name, pattern, view_callable)) + + if view_callable is not None: + if IMultiView.providedBy(view_callable): + view_callables = [ + x[1] for x in view_callable.views + ] + else: + view_callables = [view_callable] + + for view_func in view_callables: + view_callable = '%s.%s' % ( + view_func.__module__, + view_func.__name__, + ) + else: + view_callable = str(None) + + if len(route.name) > max_name: + max_name = len(route.name) + + if len(pattern) > max_pattern: + max_pattern = len(pattern) + + if len(view_callable) > max_view: + max_view = len(view_callable) + + mapped_routes.append((route.name, pattern, view_callable)) + + fmt = '%-{0}s %-{1}s %-{2}s'.format( + max_name + PAD, + max_pattern + PAD, + max_view + PAD, + ) + + for route_data in mapped_routes: + self.out(fmt % route_data) + return 0 if __name__ == '__main__': # pragma: no cover diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 12b078677..1168ba78a 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -1,9 +1,11 @@ from code import interact import optparse +import os import sys import textwrap from pyramid.compat import configparser +from pyramid.compat import exec_ from pyramid.util import DottedNameResolver from pyramid.paster import bootstrap @@ -51,6 +53,7 @@ class PShellCommand(object): loaded_objects = {} object_help = {} setup = None + pystartup = os.environ.get('PYTHONSTARTUP') def __init__(self, argv, quiet=False): self.quiet = quiet @@ -144,6 +147,12 @@ class PShellCommand(object): if shell is None: shell = self.make_shell() + if self.pystartup and os.path.isfile(self.pystartup): + with open(self.pystartup, 'rb') as fp: + exec_(fp.read().decode('utf-8'), env) + if '__builtins__' in env: + del env['__builtins__'] + try: shell(env, help) finally: diff --git a/pyramid/security.py b/pyramid/security.py index 041155563..cbb4b895f 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -17,6 +17,8 @@ Authenticated = 'system.Authenticated' Allow = 'Allow' Deny = 'Deny' +_marker = object() + class AllPermissionsList(object): """ Stand in 'permission list' to represent all permissions """ def __iter__(self): @@ -115,12 +117,12 @@ deprecated( '"effective_principals" attribute of the Pyramid request instead.' ) -def remember(request, principal, **kw): +def remember(request, userid=_marker, **kw): """ Returns a sequence of header tuples (e.g. ``[('Set-Cookie', 'foo=abc')]``) on this request's response. These headers are suitable for 'remembering' a set of credentials - implied by the data passed as ``principal`` and ``*kw`` using the + implied by the data passed as ``userid`` and ``*kw`` using the current :term:`authentication policy`. Common usage might look like so within the body of a view function (``response`` is assumed to be a :term:`WebOb` -style :term:`response` object @@ -138,11 +140,28 @@ def remember(request, principal, **kw): always return an empty sequence. If used, the composition and meaning of ``**kw`` must be agreed upon by the calling code and the effective authentication policy. + + .. deprecated:: 1.6 + Renamed the ``principal`` argument to ``userid`` to clarify its + purpose. """ + if userid is _marker: + principal = kw.pop('principal', _marker) + if principal is _marker: + raise TypeError( + 'remember() missing 1 required positional argument: ' + '\'userid\'') + else: + deprecated( + 'principal', + 'The "principal" argument was deprecated in Pyramid 1.6. ' + 'It will be removed in Pyramid 1.9. Use the "userid" ' + 'argument instead.') + userid = principal policy = _get_authentication_policy(request) if policy is None: return [] - return policy.remember(request, principal, **kw) + return policy.remember(request, userid, **kw) def forget(request): """ diff --git a/pyramid/testing.py b/pyramid/testing.py index 8cbd8b82b..f77889e72 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -79,8 +79,8 @@ class DummySecurityPolicy(object): effective_principals.extend(self.groupids) return effective_principals - def remember(self, request, principal, **kw): - self.remembered = principal + def remember(self, request, userid, **kw): + self.remembered = userid return self.remember_result def forget(self, request): diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py index e25e9faa1..c7fc1c211 100644 --- a/pyramid/tests/test_authentication.py +++ b/pyramid/tests/test_authentication.py @@ -1211,26 +1211,26 @@ class Test_parse_ticket(unittest.TestCase): self._assertRaisesBadTicket('secret', ticket, '0.0.0.0') def test_correct_with_user_data(self): - ticket = '66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!' + ticket = text_('66f9cc3e423dc57c91df696cf3d1f0d80000000auserid!a,b!') result = self._callFUT('secret', ticket, '0.0.0.0') self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) def test_correct_with_user_data_sha512(self): - ticket = '7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1160cab'\ - '0ec0e6888faa41eba641a18522b26f19109f3ffafb769767ba8a26d02aae'\ - 'ae56599a0000000auserid!a,b!' + ticket = text_('7d947cdef99bad55f8e3382a8bd089bb9dd0547f7925b7d189adc1' + '160cab0ec0e6888faa41eba641a18522b26f19109f3ffafb769767' + 'ba8a26d02aaeae56599a0000000auserid!a,b!') result = self._callFUT('secret', ticket, '0.0.0.0', 'sha512') self.assertEqual(result, (10, 'userid', ['a', 'b'], '')) def test_ipv4(self): - ticket = 'b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecdade8d7'\ - '6c530000000auserid!' + ticket = text_('b3e7156db4f8abde4439c4a6499a0668f9e7ffd7fa27b798400ecd' + 'ade8d76c530000000auserid!') result = self._callFUT('secret', ticket, '198.51.100.1', 'sha256') self.assertEqual(result, (10, 'userid', [''], '')) def test_ipv6(self): - ticket = 'd025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760cd7a2f'\ - 'a4910000000auserid!' + ticket = text_('d025b601a0f12ca6d008aa35ff3a22b7d8f3d1c1456c85becf8760' + 'cd7a2fa4910000000auserid!') result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256') self.assertEqual(result, (10, 'userid', [''], '')) pass diff --git a/pyramid/tests/test_config/pkgs/asset/subpackage/templates/bar.pt b/pyramid/tests/test_config/pkgs/asset/subpackage/templates/bar.pt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/pyramid/tests/test_config/pkgs/asset/subpackage/templates/bar.pt diff --git a/pyramid/tests/test_config/test_assets.py b/pyramid/tests/test_config/test_assets.py index 345e7f8d6..b605a602d 100644 --- a/pyramid/tests/test_config/test_assets.py +++ b/pyramid/tests/test_config/test_assets.py @@ -1,6 +1,10 @@ +import os.path import unittest from pyramid.testing import cleanUp +# we use this folder +here = os.path.dirname(os.path.abspath(__file__)) + class TestAssetsConfiguratorMixin(unittest.TestCase): def _makeOne(self, *arg, **kw): from pyramid.config import Configurator @@ -10,27 +14,31 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): def test_override_asset_samename(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_asset,'a', 'a') + self.assertRaises(ConfigurationError, config.override_asset, 'a', 'a') def test_override_asset_directory_with_file(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, - 'a:foo/', 'a:foo.pt') + 'a:foo/', + 'pyramid.tests.test_config.pkgs.asset:foo.pt') def test_override_asset_file_with_directory(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, - 'a:foo.pt', 'a:foo/') + 'a:foo.pt', + 'pyramid.tests.test_config.pkgs.asset:templates/') def test_override_asset_file_with_package(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() self.assertRaises(ConfigurationError, config.override_asset, - 'a:foo.pt', 'a') + 'a:foo.pt', + 'pyramid.tests.test_config.pkgs.asset') def test_override_asset_file_with_file(self): + from pyramid.config.assets import PackageAssetSource config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( @@ -41,10 +49,13 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/foo.pt') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/bar.pt') + source = override.source + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/bar.pt') def test_override_asset_package_with_package(self): + from pyramid.config.assets import PackageAssetSource config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( @@ -55,10 +66,13 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, '') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, '') + source = override.source + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, '') def test_override_asset_directory_with_directory(self): + from pyramid.config.assets import PackageAssetSource config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( @@ -69,10 +83,13 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/') + source = override.source + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/') def test_override_asset_directory_with_package(self): + from pyramid.config.assets import PackageAssetSource config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( @@ -83,10 +100,13 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, 'templates/') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, '') + source = override.source + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, '') def test_override_asset_package_with_directory(self): + from pyramid.config.assets import PackageAssetSource config = self._makeOne(autocommit=True) override = DummyUnderOverride() config.override_asset( @@ -97,32 +117,105 @@ class TestAssetsConfiguratorMixin(unittest.TestCase): from pyramid.tests.test_config.pkgs.asset import subpackage self.assertEqual(override.package, asset) self.assertEqual(override.path, '') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/') + source = override.source + self.assertTrue(isinstance(source, PackageAssetSource)) + self.assertEqual(source.package, subpackage) + self.assertEqual(source.prefix, 'templates/') + + def test_override_asset_directory_with_absfile(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_asset, + 'a:foo/', + os.path.join(here, 'pkgs', 'asset', 'foo.pt')) + + def test_override_asset_file_with_absdirectory(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') + self.assertRaises(ConfigurationError, config.override_asset, + 'a:foo.pt', + abspath) + + def test_override_asset_file_with_missing_abspath(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_asset, + 'a:foo.pt', + os.path.join(here, 'wont_exist')) + + def test_override_asset_file_with_absfile(self): + from pyramid.config.assets import FSAssetSource + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', + 'templates', 'bar.pt') + config.override_asset( + 'pyramid.tests.test_config.pkgs.asset:templates/foo.pt', + abspath, + _override=override) + from pyramid.tests.test_config.pkgs import asset + self.assertEqual(override.package, asset) + self.assertEqual(override.path, 'templates/foo.pt') + source = override.source + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) + + def test_override_asset_directory_with_absdirectory(self): + from pyramid.config.assets import FSAssetSource + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') + config.override_asset( + 'pyramid.tests.test_config.pkgs.asset:templates/', + abspath, + _override=override) + from pyramid.tests.test_config.pkgs import asset + self.assertEqual(override.package, asset) + self.assertEqual(override.path, 'templates/') + source = override.source + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) + + def test_override_asset_package_with_absdirectory(self): + from pyramid.config.assets import FSAssetSource + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + abspath = os.path.join(here, 'pkgs', 'asset', 'subpackage', 'templates') + config.override_asset( + 'pyramid.tests.test_config.pkgs.asset', + abspath, + _override=override) + from pyramid.tests.test_config.pkgs import asset + self.assertEqual(override.package, asset) + self.assertEqual(override.path, '') + source = override.source + self.assertTrue(isinstance(source, FSAssetSource)) + self.assertEqual(source.prefix, abspath) def test__override_not_yet_registered(self): from pyramid.interfaces import IPackageOverrides package = DummyPackage('package') - opackage = DummyPackage('opackage') + source = DummyAssetSource() config = self._makeOne() - config._override(package, 'path', opackage, 'oprefix', + config._override(package, 'path', source, PackageOverrides=DummyPackageOverrides) overrides = config.registry.queryUtility(IPackageOverrides, name='package') - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.inserted, [('path', source)]) self.assertEqual(overrides.package, package) def test__override_already_registered(self): from pyramid.interfaces import IPackageOverrides package = DummyPackage('package') - opackage = DummyPackage('opackage') + source = DummyAssetSource() overrides = DummyPackageOverrides(package) config = self._makeOne() config.registry.registerUtility(overrides, IPackageOverrides, name='package') - config._override(package, 'path', opackage, 'oprefix', + config._override(package, 'path', source, PackageOverrides=DummyPackageOverrides) - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.inserted, [('path', source)]) self.assertEqual(overrides.package, package) @@ -148,30 +241,24 @@ class TestOverrideProvider(unittest.TestCase): reg.registerUtility(overrides, IPackageOverrides, name=name) def test_get_resource_filename_no_overrides(self): - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) expected = os.path.join(here, resource_name) result = provider.get_resource_filename(None, resource_name) self.assertEqual(result, expected) def test_get_resource_stream_no_overrides(self): - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) with provider.get_resource_stream(None, resource_name) as result: _assertBody(result.read(), os.path.join(here, resource_name)) def test_get_resource_string_no_overrides(self): - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) result = provider.get_resource_string(None, resource_name) _assertBody(result, os.path.join(here, resource_name)) @@ -202,11 +289,9 @@ class TestOverrideProvider(unittest.TestCase): def test_get_resource_filename_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) expected = os.path.join(here, resource_name) result = provider.get_resource_filename(None, resource_name) self.assertEqual(result, expected) @@ -214,22 +299,18 @@ class TestOverrideProvider(unittest.TestCase): def test_get_resource_stream_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) with provider.get_resource_stream(None, resource_name) as result: _assertBody(result.read(), os.path.join(here, resource_name)) def test_get_resource_string_override_returns_None(self): overrides = DummyOverrides(None) self._registerOverrides(overrides) - import os resource_name = 'test_assets.py' import pyramid.tests.test_config provider = self._makeOne(pyramid.tests.test_config) - here = os.path.dirname(os.path.abspath(__file__)) result = provider.get_resource_string(None, resource_name) _assertBody(result, os.path.join(here, resource_name)) @@ -378,8 +459,8 @@ class TestPackageOverrides(unittest.TestCase): from pyramid.config.assets import DirectoryOverride package = DummyPackage('package') po = self._makeOne(package) - po.overrides= [None] - po.insert('foo/', 'package', 'bar/') + po.overrides = [None] + po.insert('foo/', DummyAssetSource()) self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, DirectoryOverride) @@ -388,8 +469,8 @@ class TestPackageOverrides(unittest.TestCase): from pyramid.config.assets import FileOverride package = DummyPackage('package') po = self._makeOne(package) - po.overrides= [None] - po.insert('foo.pt', 'package', 'bar.pt') + po.overrides = [None] + po.insert('foo.pt', DummyAssetSource()) self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, FileOverride) @@ -399,132 +480,137 @@ class TestPackageOverrides(unittest.TestCase): from pyramid.config.assets import DirectoryOverride package = DummyPackage('package') po = self._makeOne(package) - po.overrides= [None] - po.insert('', 'package', 'bar/') + po.overrides = [None] + source = DummyAssetSource() + po.insert('', source) self.assertEqual(len(po.overrides), 2) override = po.overrides[0] self.assertEqual(override.__class__, DirectoryOverride) - def test_search_path(self): - overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))] + def test_filtered_sources(self): + overrides = [ DummyOverride(None), DummyOverride('foo')] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides - self.assertEqual(list(po.search_path('whatever')), - [('package', 'name')]) + po.overrides = overrides + self.assertEqual(list(po.filtered_sources('whatever')), ['foo']) def test_get_filename(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'test_assets.py'))] + source = DummyAssetSource(filename='foo.pt') + overrides = [ DummyOverride(None), DummyOverride((source, ''))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - expected = os.path.join(here, 'test_assets.py') - self.assertEqual(po.get_filename('whatever'), expected) + po.overrides = overrides + result = po.get_filename('whatever') + self.assertEqual(result, 'foo.pt') + self.assertEqual(source.resource_name, '') def test_get_filename_file_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(filename=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.get_filename('whatever'), None) - + self.assertEqual(source.resource_name, 'wont_exist') + def test_get_stream(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'test_assets.py'))] + source = DummyAssetSource(stream='a stream?') + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - with po.get_stream('whatever') as stream: - _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) + po.overrides = overrides + self.assertEqual(po.get_stream('whatever'), 'a stream?') + self.assertEqual(source.resource_name, 'foo.pt') def test_get_stream_file_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(stream=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.get_stream('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') def test_get_string(self): - import os - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'test_assets.py'))] + source = DummyAssetSource(string='a string') + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides - here = os.path.dirname(os.path.abspath(__file__)) - _assertBody(po.get_string('whatever'), - os.path.join(here, 'test_assets.py')) + po.overrides = overrides + self.assertEqual(po.get_string('whatever'), 'a string') + self.assertEqual(source.resource_name, 'foo.pt') def test_get_string_file_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(string=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.get_string('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') def test_has_resource(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'test_assets.py'))] + source = DummyAssetSource(exists=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.has_resource('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') def test_has_resource_file_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(exists=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.has_resource('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') def test_isdir_false(self): - overrides = [ DummyOverride( - ('pyramid.tests.test_config', 'test_assets.py'))] + source = DummyAssetSource(isdir=False) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.isdir('whatever'), False) - + self.assertEqual(source.resource_name, 'foo.pt') + def test_isdir_true(self): - overrides = [ DummyOverride( - ('pyramid.tests.test_config', 'files'))] + source = DummyAssetSource(isdir=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.isdir('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') def test_isdir_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(isdir=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.isdir('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') def test_listdir(self): - overrides = [ DummyOverride( - ('pyramid.tests.test_config', 'files'))] + source = DummyAssetSource(listdir=True) + overrides = [DummyOverride(None), DummyOverride((source, 'foo.pt'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides - self.assertTrue(po.listdir('whatever')) + po.overrides = overrides + self.assertEqual(po.listdir('whatever'), True) + self.assertEqual(source.resource_name, 'foo.pt') def test_listdir_doesnt_exist(self): - overrides = [ DummyOverride(None), DummyOverride( - ('pyramid.tests.test_config', 'wont_exist'))] + source = DummyAssetSource(listdir=None) + overrides = [DummyOverride(None), DummyOverride((source, 'wont_exist'))] package = DummyPackage('package') po = self._makeOne(package) - po.overrides= overrides + po.overrides = overrides self.assertEqual(po.listdir('whatever'), None) + self.assertEqual(source.resource_name, 'wont_exist') # PEP 302 __loader__ extensions: use the "real" __loader__, if present. def test_get_data_pkg_has_no___loader__(self): @@ -570,27 +656,124 @@ class TestPackageOverrides(unittest.TestCase): def test_get_source_pkg_has___loader__(self): package = DummyPackage('package') - loader = package.__loader__ = DummyLoader() + loader = package.__loader__ = DummyLoader() po = self._makeOne(package) self.assertEqual(po.get_source('whatever'), 'def foo():\n pass') self.assertEqual(loader._got_source, 'whatever') +class AssetSourceIntegrationTests(object): + + def test_get_filename(self): + source = self._makeOne('') + self.assertEqual(source.get_filename('test_assets.py'), + os.path.join(here, 'test_assets.py')) + + def test_get_filename_with_prefix(self): + source = self._makeOne('test_assets.py') + self.assertEqual(source.get_filename(''), + os.path.join(here, 'test_assets.py')) + + def test_get_filename_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_filename('wont_exist'), None) + + def test_get_stream(self): + source = self._makeOne('') + with source.get_stream('test_assets.py') as stream: + _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) + + def test_get_stream_with_prefix(self): + source = self._makeOne('test_assets.py') + with source.get_stream('') as stream: + _assertBody(stream.read(), os.path.join(here, 'test_assets.py')) + + def test_get_stream_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_stream('wont_exist'), None) + + def test_get_string(self): + source = self._makeOne('') + _assertBody(source.get_string('test_assets.py'), + os.path.join(here, 'test_assets.py')) + + def test_get_string_with_prefix(self): + source = self._makeOne('test_assets.py') + _assertBody(source.get_string(''), + os.path.join(here, 'test_assets.py')) + + def test_get_string_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.get_string('wont_exist'), None) + + def test_exists(self): + source = self._makeOne('') + self.assertEqual(source.exists('test_assets.py'), True) + + def test_exists_with_prefix(self): + source = self._makeOne('test_assets.py') + self.assertEqual(source.exists(''), True) + + def test_exists_file_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.exists('wont_exist'), None) + + def test_isdir_false(self): + source = self._makeOne('') + self.assertEqual(source.isdir('test_assets.py'), False) + + def test_isdir_true(self): + source = self._makeOne('') + self.assertEqual(source.isdir('files'), True) + + def test_isdir_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.isdir('wont_exist'), None) + + def test_listdir(self): + source = self._makeOne('') + self.assertTrue(source.listdir('files')) + + def test_listdir_doesnt_exist(self): + source = self._makeOne('') + self.assertEqual(source.listdir('wont_exist'), None) + +class TestPackageAssetSource(AssetSourceIntegrationTests, unittest.TestCase): + + def _getTargetClass(self): + from pyramid.config.assets import PackageAssetSource + return PackageAssetSource + + def _makeOne(self, prefix, package='pyramid.tests.test_config'): + klass = self._getTargetClass() + return klass(package, prefix) + +class TestFSAssetSource(AssetSourceIntegrationTests, unittest.TestCase): + def _getTargetClass(self): + from pyramid.config.assets import FSAssetSource + return FSAssetSource + + def _makeOne(self, prefix, base_prefix=here): + klass = self._getTargetClass() + return klass(os.path.join(base_prefix, prefix)) + class TestDirectoryOverride(unittest.TestCase): def _getTargetClass(self): from pyramid.config.assets import DirectoryOverride return DirectoryOverride - def _makeOne(self, path, package, prefix): + def _makeOne(self, path, source): klass = self._getTargetClass() - return klass(path, package, prefix) + return klass(path, source) def test_it_match(self): - o = self._makeOne('foo/', 'package', 'bar/') + source = DummyAssetSource() + o = self._makeOne('foo/', source) result = o('foo/something.pt') - self.assertEqual(result, ('package', 'bar/something.pt')) + self.assertEqual(result, (source, 'something.pt')) def test_it_no_match(self): - o = self._makeOne('foo/', 'package', 'bar/') + source = DummyAssetSource() + o = self._makeOne('foo/', source) result = o('baz/notfound.pt') self.assertEqual(result, None) @@ -599,17 +782,19 @@ class TestFileOverride(unittest.TestCase): from pyramid.config.assets import FileOverride return FileOverride - def _makeOne(self, path, package, prefix): + def _makeOne(self, path, source): klass = self._getTargetClass() - return klass(path, package, prefix) + return klass(path, source) def test_it_match(self): - o = self._makeOne('foo.pt', 'package', 'bar.pt') + source = DummyAssetSource() + o = self._makeOne('foo.pt', source) result = o('foo.pt') - self.assertEqual(result, ('package', 'bar.pt')) + self.assertEqual(result, (source, '')) def test_it_no_match(self): - o = self._makeOne('foo.pt', 'package', 'bar.pt') + source = DummyAssetSource() + o = self._makeOne('foo.pt', source) result = o('notfound.pt') self.assertEqual(result, None) @@ -634,8 +819,8 @@ class DummyPackageOverrides: self.package = package self.inserted = [] - def insert(self, path, package, prefix): - self.inserted.append((path, package, prefix)) + def insert(self, path, source): + self.inserted.append((path, source)) class DummyPkgResources: def __init__(self): @@ -647,6 +832,34 @@ class DummyPkgResources: class DummyPackage: def __init__(self, name): self.__name__ = name + +class DummyAssetSource: + def __init__(self, **kw): + self.kw = kw + + def get_filename(self, resource_name): + self.resource_name = resource_name + return self.kw['filename'] + + def get_stream(self, resource_name): + self.resource_name = resource_name + return self.kw['stream'] + + def get_string(self, resource_name): + self.resource_name = resource_name + return self.kw['string'] + + def exists(self, resource_name): + self.resource_name = resource_name + return self.kw['exists'] + + def isdir(self, resource_name): + self.resource_name = resource_name + return self.kw['isdir'] + + def listdir(self, resource_name): + self.resource_name = resource_name + return self.kw['listdir'] class DummyLoader: _got_data = _is_package = None @@ -664,12 +877,10 @@ class DummyLoader: return 'def foo():\n pass' class DummyUnderOverride: - def __call__(self, package, path, override_package, override_prefix, - _info=''): + def __call__(self, package, path, source, _info=''): self.package = package self.path = path - self.override_package = override_package - self.override_prefix = override_prefix + self.source = source def read_(src): with open(src, 'rb') as f: diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index 39b8ba70d..b0d03fb72 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -3898,6 +3898,13 @@ class TestStaticURLInfo(unittest.TestCase): ('http://example.com/', 'anotherpackage:path/', None, None)] self._assertRegistrations(config, expected) + def test_add_package_root(self): + inst = self._makeOne() + config = self._makeConfig() + inst.add(config, 'http://example.com', 'package:') + expected = [('http://example.com/', 'package:', None, None)] + self._assertRegistrations(config, expected) + def test_add_url_withendslash(self): inst = self._makeOne() config = self._makeConfig() diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 35648ed38..c2786c391 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -81,7 +81,7 @@ class StaticAppBase(IntegrationBase): res = self.testapp.get('/static/.hiddenfile', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/static/.hiddenfile')) - if defaultlocale is not None: + if defaultlocale is not None: # pragma: no cover # These tests are expected to fail on LANG=C systems due to decode # errors and on non-Linux systems due to git highchar handling # vagaries diff --git a/pyramid/tests/test_registry.py b/pyramid/tests/test_registry.py index 11019b852..50f49f24d 100644 --- a/pyramid/tests/test_registry.py +++ b/pyramid/tests/test_registry.py @@ -12,6 +12,11 @@ class TestRegistry(unittest.TestCase): registry = self._makeOne() self.assertEqual(registry.__nonzero__(), True) + def test_package_name(self): + package_name = 'testing' + registry = self._getTargetClass()(package_name) + self.assertEqual(registry.package_name, package_name) + def test_registerHandler_and_notify(self): registry = self._makeOne() self.assertEqual(registry.has_listeners, False) diff --git a/pyramid/tests/test_scripts/pystartup.py b/pyramid/tests/test_scripts/pystartup.py new file mode 100644 index 000000000..c4e5bcc80 --- /dev/null +++ b/pyramid/tests/test_scripts/pystartup.py @@ -0,0 +1 @@ +foo = 1 diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py index 25a3cd2e3..32202af4b 100644 --- a/pyramid/tests/test_scripts/test_proutes.py +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -123,8 +123,53 @@ class TestPRoutesCommand(unittest.TestCase): self.assertEqual(result, 0) self.assertEqual(len(L), 3) compare_to = L[-1].split()[:3] - self.assertEqual(compare_to, ['a', '/a', '<function']) - + self.assertEqual( + compare_to, + ['a', '/a', 'pyramid.tests.test_scripts.test_proutes.view'] + ) + + def test_one_route_with_long_name_one_view_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + registry = Registry() + def view():pass + + class IMyRoute(Interface): + pass + + registry.registerAdapter( + view, + (IViewClassifier, IMyRoute, Interface), + IView, '' + ) + + registry.registerUtility(IMyRoute, IRouteRequest, + name='very_long_name_123') + + command = self._makeOne() + route = dummy.DummyRoute( + 'very_long_name_123', + '/and_very_long_pattern_as_well' + ) + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (dummy.DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split()[:3] + self.assertEqual( + compare_to, + ['very_long_name_123', + '/and_very_long_pattern_as_well', + 'pyramid.tests.test_scripts.test_proutes.view'] + ) + def test_single_route_one_view_registered_with_factory(self): from zope.interface import Interface from pyramid.registry import Registry @@ -154,6 +199,47 @@ class TestPRoutesCommand(unittest.TestCase): self.assertEqual(len(L), 3) self.assertEqual(L[-1].split()[:3], ['a', '/a', '<unknown>']) + def test_single_route_multiview_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + + registry = Registry() + + def view(): pass + + class IMyRoute(Interface): + pass + + multiview1 = dummy.DummyMultiView( + view, context='context', + view_name='a1' + ) + + registry.registerAdapter( + multiview1, + (IViewClassifier, IMyRoute, Interface), + IMultiView, '' + ) + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (dummy.DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, 0) + self.assertEqual(len(L), 3) + compare_to = L[-1].split()[:3] + self.assertEqual( + compare_to, + ['a', '/a', 'pyramid.tests.test_scripts.test_proutes.view'] + ) + def test__get_mapper(self): from pyramid.registry import Registry from pyramid.urldispatch import RoutesMapper @@ -161,7 +247,7 @@ class TestPRoutesCommand(unittest.TestCase): registry = Registry() result = command._get_mapper(registry) self.assertEqual(result.__class__, RoutesMapper) - + class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.proutes import main @@ -170,4 +256,3 @@ class Test_main(unittest.TestCase): def test_it(self): result = self._callFUT(['proutes']) self.assertEqual(result, 2) - diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index 7cb130c41..a6ba2eaea 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -1,3 +1,4 @@ +import os import unittest from pyramid.tests.test_scripts import dummy @@ -24,6 +25,9 @@ class TestPShellCommand(unittest.TestCase): self.options.python_shell = '' self.options.setup = None cmd.options = self.options + # default to None to prevent side-effects from running tests in + # unknown environments + cmd.pystartup = None return cmd def test_make_default_shell(self): @@ -369,6 +373,25 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) + def test_command_loads_pythonstartup(self): + command = self._makeOne() + command.pystartup = ( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), + 'pystartup.py'))) + shell = dummy.DummyShell() + command.run(shell) + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'foo':1, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) class Test_main(unittest.TestCase): def _callFUT(self, argv): diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py index 6f08a100c..6d75ac8e3 100644 --- a/pyramid/tests/test_security.py +++ b/pyramid/tests/test_security.py @@ -134,9 +134,9 @@ class TestRemember(unittest.TestCase): def tearDown(self): testing.tearDown() - def _callFUT(self, *arg): + def _callFUT(self, *arg, **kwarg): from pyramid.security import remember - return remember(*arg) + return remember(*arg, **kwarg) def test_no_authentication_policy(self): request = _makeRequest() @@ -159,6 +159,19 @@ class TestRemember(unittest.TestCase): result = self._callFUT(request, 'me') self.assertEqual(result, [('X-Pyramid-Test', 'me')]) + def test_with_deprecated_principal_arg(self): + request = _makeRequest() + registry = request.registry + _registerAuthenticationPolicy(registry, 'yo') + result = self._callFUT(request, principal='me') + self.assertEqual(result, [('X-Pyramid-Test', 'me')]) + + def test_with_missing_arg(self): + request = _makeRequest() + registry = request.registry + _registerAuthenticationPolicy(registry, 'yo') + self.assertRaises(TypeError, lambda: self._callFUT(request)) + class TestForget(unittest.TestCase): def setUp(self): testing.setUp() @@ -462,8 +475,8 @@ class DummyAuthenticationPolicy: def authenticated_userid(self, request): return self.result - def remember(self, request, principal, **kw): - headers = [(_TEST_HEADER, principal)] + def remember(self, request, userid, **kw): + headers = [(_TEST_HEADER, userid)] self._header_remembered = headers[0] return headers diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 2ca4c4a66..a18fa8d16 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -217,6 +217,49 @@ class Test_WeakOrderedSet(unittest.TestCase): self.assertEqual(list(wos), []) self.assertEqual(wos.last, None) +class Test_strings_differ(unittest.TestCase): + def _callFUT(self, *args, **kw): + from pyramid.util import strings_differ + return strings_differ(*args, **kw) + + def test_it(self): + self.assertFalse(self._callFUT(b'foo', b'foo')) + self.assertTrue(self._callFUT(b'123', b'345')) + self.assertTrue(self._callFUT(b'1234', b'123')) + self.assertTrue(self._callFUT(b'123', b'1234')) + + def test_it_with_internal_comparator(self): + result = self._callFUT(b'foo', b'foo', compare_digest=None) + self.assertFalse(result) + + result = self._callFUT(b'123', b'abc', compare_digest=None) + self.assertTrue(result) + + def test_it_with_external_comparator(self): + class DummyComparator(object): + called = False + def __init__(self, ret_val): + self.ret_val = ret_val + + def __call__(self, a, b): + self.called = True + return self.ret_val + + dummy_compare = DummyComparator(True) + result = self._callFUT(b'foo', b'foo', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertFalse(result) + + dummy_compare = DummyComparator(False) + result = self._callFUT(b'123', b'345', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertTrue(result) + + dummy_compare = DummyComparator(False) + result = self._callFUT(b'abc', b'abc', compare_digest=dummy_compare) + self.assertTrue(dummy_compare.called) + self.assertTrue(result) + class Test_object_description(unittest.TestCase): def _callFUT(self, object): from pyramid.util import object_description diff --git a/pyramid/util.py b/pyramid/util.py index 6b92f17fc..6de53d559 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -1,4 +1,9 @@ import functools +try: + # py2.7.7+ and py3.3+ have native comparison support + from hmac import compare_digest +except ImportError: # pragma: nocover + compare_digest = None import inspect import traceback import weakref @@ -227,7 +232,7 @@ class WeakOrderedSet(object): oid = self._order[-1] return self._items[oid]() -def strings_differ(string1, string2): +def strings_differ(string1, string2, compare_digest=compare_digest): """Check whether two strings differ while avoiding timing attacks. This function returns True if the given strings differ and False @@ -237,14 +242,25 @@ def strings_differ(string1, string2): http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf - """ - if len(string1) != len(string2): - return True - - invalid_bits = 0 - for a, b in zip(string1, string2): - invalid_bits += a != b + .. versionchanged:: 1.6 + Support :func:`hmac.compare_digest` if it is available (Python 2.7.7+ + and Python 3.3+). + """ + len_eq = len(string1) == len(string2) + if len_eq: + invalid_bits = 0 + left = string1 + else: + invalid_bits = 1 + left = string2 + right = string2 + + if compare_digest is not None: + invalid_bits += not compare_digest(left, right) + else: + for a, b in zip(left, right): + invalid_bits += a != b return invalid_bits != 0 def object_description(object): @@ -7,6 +7,7 @@ where=pyramid nocapture=1 cover-package=pyramid cover-erase=1 +cover-min-percentage=100 [aliases] dev = develop easy_install pyramid[testing] @@ -1,6 +1,6 @@ [tox] envlist = - py26,py27,py32,py33,py34,pypy,cover + py26,py27,py32,py33,py34,pypy,pypy3,cover [testenv] commands = |
