From 7d467162fbfeffde03cca6a7330acb5eece4666a Mon Sep 17 00:00:00 2001 From: "Deployment Bot (from Travis CI)" Date: Tue, 21 Apr 2020 19:59:17 +0000 Subject: [PATCH] Deploy opa-stack/opa-stack.github.io to github.com/opa-stack/opa-stack.github.io.git:master --- 404.html | 20 + assets/css/0.styles.a2b7d327.css | 1 + assets/img/search.83621669.svg | 1 + assets/js/10.15db5ece.js | 1 + assets/js/11.b8f9ff69.js | 1 + assets/js/12.3a8e2acb.js | 1 + assets/js/13.e8dd8de7.js | 1 + assets/js/14.5b8196d5.js | 1 + assets/js/15.79dc74f3.js | 1 + assets/js/16.bff4d9f4.js | 1 + assets/js/17.7b74f075.js | 1 + assets/js/18.718f9d0a.js | 1 + assets/js/3.ab9a3b8e.js | 1 + assets/js/4.ae135471.js | 1 + assets/js/5.3e16bd23.js | 1 + assets/js/6.52732cdc.js | 1 + assets/js/7.b108359b.js | 1 + assets/js/8.1da2c1df.js | 1 + assets/js/9.76f32432.js | 1 + assets/js/app.4e27d91f.js | 14 + assets/js/vendors~docsearch.abc87a80.js | 3 + guide/api/configuration.html | 175 ++++++++ guide/api/plugin-system.html | 530 +++++++++++++++++++++++ guide/development.html | 113 +++++ guide/examples.html | 330 ++++++++++++++ guide/index.html | 73 ++++ guide/new-project.html | 76 ++++ guide/optional-components-reference.html | 182 ++++++++ guide/plugins.html | 253 +++++++++++ index.html | 74 ++++ more-info/contact.html | 70 +++ more-info/index.html | 73 ++++ opa-stack/index.html | 66 +++ 33 files changed, 2070 insertions(+) create mode 100644 404.html create mode 100644 assets/css/0.styles.a2b7d327.css create mode 100644 assets/img/search.83621669.svg create mode 100644 assets/js/10.15db5ece.js create mode 100644 assets/js/11.b8f9ff69.js create mode 100644 assets/js/12.3a8e2acb.js create mode 100644 assets/js/13.e8dd8de7.js create mode 100644 assets/js/14.5b8196d5.js create mode 100644 assets/js/15.79dc74f3.js create mode 100644 assets/js/16.bff4d9f4.js create mode 100644 assets/js/17.7b74f075.js create mode 100644 assets/js/18.718f9d0a.js create mode 100644 assets/js/3.ab9a3b8e.js create mode 100644 assets/js/4.ae135471.js create mode 100644 assets/js/5.3e16bd23.js create mode 100644 assets/js/6.52732cdc.js create mode 100644 assets/js/7.b108359b.js create mode 100644 assets/js/8.1da2c1df.js create mode 100644 assets/js/9.76f32432.js create mode 100644 assets/js/app.4e27d91f.js create mode 100644 assets/js/vendors~docsearch.abc87a80.js create mode 100644 guide/api/configuration.html create mode 100644 guide/api/plugin-system.html create mode 100644 guide/development.html create mode 100644 guide/examples.html create mode 100644 guide/index.html create mode 100644 guide/new-project.html create mode 100644 guide/optional-components-reference.html create mode 100644 guide/plugins.html create mode 100644 index.html create mode 100644 more-info/contact.html create mode 100644 more-info/index.html create mode 100644 opa-stack/index.html diff --git a/404.html b/404.html new file mode 100644 index 0000000..c40455e --- /dev/null +++ b/404.html @@ -0,0 +1,20 @@ + + + + + + opa-stack + + + + + + + + +

404

That's a Four-Oh-Four.
+ Take me home. +
+ + + diff --git a/assets/css/0.styles.a2b7d327.css b/assets/css/0.styles.a2b7d327.css new file mode 100644 index 0000000..dc97e17 --- /dev/null +++ b/assets/css/0.styles.a2b7d327.css @@ -0,0 +1 @@ +#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border-color:#3eaf7c transparent transparent #3eaf7c;border-style:solid;border-width:2px;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.go-to-top[data-v-5fd4ef0c]{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;color:#3eaf7c;z-index:1}.go-to-top[data-v-5fd4ef0c]:hover{color:#72cda4}@media (max-width:959px){.go-to-top[data-v-5fd4ef0c]{display:none}}.fade-enter-active[data-v-5fd4ef0c],.fade-leave-active[data-v-5fd4ef0c]{transition:opacity .3s}.fade-enter[data-v-5fd4ef0c],.fade-leave-to[data-v-5fd4ef0c]{opacity:0}.icon.outbound{color:#aaa;display:inline-block;vertical-align:middle;position:relative;top:-1px}.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.algolia-search-wrapper>span{vertical-align:middle}.algolia-search-wrapper .algolia-autocomplete{line-height:normal}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu{background-color:#fff;border:1px solid #999;border-radius:4px;font-size:16px;margin:6px 0 0;padding:4px;text-align:left}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu:before{border-color:#999}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu [class*=ds-dataset-]{border:none;padding:0}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu .ds-suggestions{margin-top:0}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu .ds-suggestion{border-bottom:1px solid #eaecef}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#2c815b}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion{border-color:#eaecef;padding:0}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--category-header{padding:5px 10px;margin-top:0;background:#3eaf7c;color:#fff;font-weight:600}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight{background:hsla(0,0%,100%,.6)}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--wrapper{padding:0}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--title{font-weight:600;margin-bottom:0;color:#2c3e50}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{vertical-align:top;padding:5px 7px 5px 5px;border-color:#eaecef;background:#f1f3f5}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{display:none}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column-text{color:#555}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-footer{border-color:#eaecef}.algolia-search-wrapper .algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--content{background-color:#e7edf3!important;color:#2c3e50}@media (min-width:719px){.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{float:none;width:150px;min-width:150px;display:table-cell}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{float:none;display:table-cell;width:100%;vertical-align:top}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .ds-dropdown-menu{min-width:515px!important}}@media (max-width:719px){.algolia-search-wrapper .ds-dropdown-menu{min-width:calc(100vw - 4rem)!important;max-width:calc(100vw - 4rem)!important}.algolia-search-wrapper .algolia-docsearch-suggestion--wrapper{padding:5px 7px 5px 5px!important}.algolia-search-wrapper .algolia-docsearch-suggestion--subcategory-column{padding:0!important;background:#fff!important}.algolia-search-wrapper .algolia-docsearch-suggestion--subcategory-column-text:after{content:" > ";font-size:10px;line-height:14.4px;display:inline-block;width:5px;margin:-3px 3px 0;vertical-align:middle}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;height:2rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (-ms-high-contrast:none){.search-box input{height:2rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.sidebar-button{cursor:pointer;display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:#2c3e50}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .dropdown-title{font-weight:600;font-size:inherit}.dropdown-wrapper .dropdown-title:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper.open .nav-dropdown,.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper.open:blur{display:none}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid;border-color:#ddd #ddd #ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.page-edit{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit{padding:2rem}}@media (max-width:419px){.page-edit{padding:1.5rem}}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-nav{padding:2rem}}@media (max-width:419px){.page-nav{padding:1.5rem}}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}.page{padding-bottom:2rem;display:block}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading:not(.clickable){cursor:auto;color:inherit}.sidebar-group.is-sub-group{padding-left:0}.sidebar-group.is-sub-group>.sidebar-heading{font-size:.95em;line-height:1.4;font-weight:400;padding-left:2rem}.sidebar-group.is-sub-group>.sidebar-heading:not(.clickable){opacity:.5}.sidebar-group.is-sub-group>.sidebar-group-items{padding-left:1rem}.sidebar-group.is-sub-group>.sidebar-group-items>li>.sidebar-link{font-size:.95em;border-left:none}.sidebar-group.depth-2>.sidebar-heading{border-left:none}.sidebar-heading{color:#2c3e50;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0;border-left:.25rem solid transparent}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading.clickable.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-heading.clickable:hover{color:#3eaf7c}.sidebar-group-items{transition:height .1s ease-out;font-size:.95em;overflow:hidden}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-size:1em;font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar>.sidebar-links{padding:1.5rem 0}.sidebar>.sidebar-links>li>a.sidebar-link{font-size:1.1em;line-height:1.7;font-weight:700}.sidebar>.sidebar-links>li:not(:first-child){margin-top:.75rem}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar>.sidebar-links{padding:1rem 0}}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.theme-default-content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.theme-default-content code .token.deleted{color:#ec5975}.theme-default-content code .token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.theme-default-content pre[class*=language-] code,.theme-default-content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;-webkit-user-select:none;-ms-user-select:none;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-docker]:before{content:"docker"}div[class~=language-dockerfile]:before{content:"dockerfile"}div[class~=language-makefile]:before{content:"makefile"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}div[class~=language-php]:before{content:"php"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.custom-block.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:#eee}.custom-block.details h4{margin-top:0}.custom-block.details figure:last-child,.custom-block.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-block.details summary{outline:none;cursor:pointer}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.theme-default-content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.theme-default-content:not(.custom){padding:2rem}}@media (max-width:419px){.theme-default-content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0;background-color:#fff}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:16px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.theme-default-content:not(.custom)>:first-child{margin-top:3.6rem}.theme-default-content:not(.custom) a:hover{text-decoration:underline}.theme-default-content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.theme-default-content:not(.custom) img{max-width:100%}.theme-default-content.custom{padding:0;margin:0}.theme-default-content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1rem;color:#999;border-left:.2rem solid #dfe2e5;margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p{margin:0}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.theme-default-content:not(.custom)>h1,.theme-default-content:not(.custom)>h2,.theme-default-content:not(.custom)>h3,.theme-default-content:not(.custom)>h4,.theme-default-content:not(.custom)>h5,.theme-default-content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.theme-default-content:not(.custom)>h1:first-child,.theme-default-content:not(.custom)>h2:first-child,.theme-default-content:not(.custom)>h3:first-child,.theme-default-content:not(.custom)>h4:first-child,.theme-default-content:not(.custom)>h5:first-child,.theme-default-content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.theme-default-content:not(.custom)>h1:first-child+.custom-block,.theme-default-content:not(.custom)>h1:first-child+p,.theme-default-content:not(.custom)>h1:first-child+pre,.theme-default-content:not(.custom)>h2:first-child+.custom-block,.theme-default-content:not(.custom)>h2:first-child+p,.theme-default-content:not(.custom)>h2:first-child+pre,.theme-default-content:not(.custom)>h3:first-child+.custom-block,.theme-default-content:not(.custom)>h3:first-child+p,.theme-default-content:not(.custom)>h3:first-child+pre,.theme-default-content:not(.custom)>h4:first-child+.custom-block,.theme-default-content:not(.custom)>h4:first-child+p,.theme-default-content:not(.custom)>h4:first-child+pre,.theme-default-content:not(.custom)>h5:first-child+.custom-block,.theme-default-content:not(.custom)>h5:first-child+p,.theme-default-content:not(.custom)>h5:first-child+pre,.theme-default-content:not(.custom)>h6:first-child+.custom-block,.theme-default-content:not(.custom)>h6:first-child+p,.theme-default-content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .theme-default-content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}html{overflow-y:scroll}img+.icon.outbound{display:none}.badge[data-v-15b7b770]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff}.badge.green[data-v-15b7b770],.badge.tip[data-v-15b7b770],.badge[data-v-15b7b770]{background-color:#42b983}.badge.error[data-v-15b7b770]{background-color:#da5961}.badge.warn[data-v-15b7b770],.badge.warning[data-v-15b7b770],.badge.yellow[data-v-15b7b770]{background-color:#e7c000}.badge+.badge[data-v-15b7b770]{margin-left:5px}.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-webkit-input-placeholder{color:#aaa}.searchbox__input:-ms-input-placeholder{color:#aaa}.searchbox__input::-ms-input-placeholder{color:#aaa}.searchbox__input::-moz-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes sbx-reset-in{0%{transform:translate3d(-20%,0,0);opacity:0}to{transform:none;opacity:1}}@keyframes sbx-reset-in{0%{transform:translate3d(-20%,0,0);opacity:0}to{transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}}@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 012.966 2.966V20.5a2.967 2.967 0 01-2.966 2.964H78.988a2.967 2.967 0 01-2.966-2.964V3.897A2.961 2.961 0 0178.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 00-1.574-.199 5.7 5.7 0 00-.897.069 2.699 2.699 0 00-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 01-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 01-1.471-.636 3.085 3.085 0 01-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 011.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 011.82-.185 8.404 8.404 0 011.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 00-.384-.73 1.784 1.784 0 00-.724-.493 3.164 3.164 0 00-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 00-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 012.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 00-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 00-.814.24 1.46 1.46 0 00-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 01.233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 01-1.471-.635 3.085 3.085 0 01-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 012.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 00-.109-.875 1.873 1.873 0 00-.384-.731 1.784 1.784 0 00-.724-.492 3.165 3.165 0 00-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 00-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 012.073-.177zm-8.034-1.271a1.626 1.626 0 01-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 01-1.128 1.906 4.986 4.986 0 01-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 01-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 01-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 011.15-1.892 5.133 5.133 0 011.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 011.753 1.216 5.644 5.644 0 011.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 00-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 01-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 01-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 012.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 00-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 01-.582-.271 13.67 13.67 0 01-.55-.287 4.275 4.275 0 01-.567-.351 6.92 6.92 0 01-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 01-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 00-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 00-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 00-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 01-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 00-.978-.977h-2.28a.978.978 0 00-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 011.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 00-1.382 0l-.465.465a.973.973 0 000 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 00-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 01-4.49-4.482 4.488 4.488 0 014.49-4.482 4.488 4.488 0 014.489 4.482 4.484 4.484 0 01-4.49 4.482m0-10.85a6.363 6.363 0 100 12.729 6.37 6.37 0 006.372-6.368 6.358 6.358 0 00-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} \ No newline at end of file diff --git a/assets/img/search.83621669.svg b/assets/img/search.83621669.svg new file mode 100644 index 0000000..03d8391 --- /dev/null +++ b/assets/img/search.83621669.svg @@ -0,0 +1 @@ + diff --git a/assets/js/10.15db5ece.js b/assets/js/10.15db5ece.js new file mode 100644 index 0000000..1481297 --- /dev/null +++ b/assets/js/10.15db5ece.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{220:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"development"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#development"}},[t._v("#")]),t._v(" Development")]),t._v(" "),a("h2",{attrs:{id:"commands"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#commands"}},[t._v("#")]),t._v(" Commands")]),t._v(" "),a("ul",[a("li",[a("p",[t._v("Getting a mongo-shell")]),t._v(" "),a("ul",[a("li",[t._v("docker-compose exec mongo mongo mongodb://mongo:mongo@localhost:27017/api")])])]),t._v(" "),a("li",[a("p",[t._v("Stop api and run manually")]),t._v(" "),a("ul",[a("li",[t._v("s6-svc -dq /var/run/s6/services/api/")]),t._v(" "),a("li",[t._v("cd /data/\n"),a("ul",[a("li",[t._v("uvicorn opa.main:app --host 0.0.0.0")]),t._v(" "),a("li",[t._v("OPA_PLUGIN_PATHS=/opa-stack/examples/docker-compose/celery-task/plugins/ uvicorn opa.main:app --host 0.0.0.0 --log-level debug")])])])])])]),t._v(" "),a("h2",{attrs:{id:"resources"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#resources"}},[t._v("#")]),t._v(" Resources")]),t._v(" "),a("ul",[a("li",[t._v("Cheatsheet - https://gitlab.com/euri10/fastapi_cheatsheet")])]),t._v(" "),a("h2",{attrs:{id:"builtin-libs-utils"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#builtin-libs-utils"}},[t._v("#")]),t._v(" Builtin libs/utils")]),t._v(" "),a("h3",{attrs:{id:"logging"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#logging"}},[t._v("#")]),t._v(" Logging")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("from opa import log")])])]),t._v(" "),a("p",[t._v("The log object will behave just like a "),a("a",{attrs:{href:"https://github.com/Delgan/loguru",target:"_blank",rel:"noopener noreferrer"}},[t._v("loguru"),a("OutboundLink")],1),t._v(" object. In addition to use the "),a("code",[t._v("log.info('...')")]),t._v(" and other log-function, you can also use it in after an object to get additional quick info.")]),t._v(" "),a("p",[t._v("Example")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" log\n\nlog"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'b'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" log "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Will output the object `a` as `DEBUG`.")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'b'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# [1, 2, 3]")]),t._v("\n")])])]),a("h2",{attrs:{id:"development-mode-env-dev"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#development-mode-env-dev"}},[t._v("#")]),t._v(" Development mode (ENV=dev)")]),t._v(" "),a("ul",[a("li",[t._v("Currently, only the "),a("code",[t._v("api")]),t._v(" does something when "),a("code",[t._v("ENV")]),t._v(" is set.. But this might change when more components are added later.")])]),t._v(" "),a("p",[t._v("On dev, if you set "),a("code",[t._v("ENV")]),t._v(" to "),a("code",[t._v("dev")]),t._v(", a "),a("RouterLink",{attrs:{to:"/guide/api/configuration.html#dev"}},[t._v("few things happens")]),t._v(", some of them are")],1),t._v(" "),a("h3",{attrs:{id:"ptvsd"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#ptvsd"}},[t._v("#")]),t._v(" PTVSD")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/Microsoft/ptvsd/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Python Tools for Visual Studio debug server"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("The port "),a("code",[t._v("5678")]),t._v(" will be exposed, and you can connect example vscode to it so it can help you debug your code.\nYou will be able to set breakpoints, and do the regular debugging in vscode. This also works inside async code.")]),t._v(" "),a("p",[t._v("Example-configuration ("),a("code",[t._v("launch.json")]),t._v(") for vscode if you use docker-compose and expose the port like "),a("code",[t._v('"127.0.0.1:5678:5678"')])]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"version"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.2.0"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"configurations"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"name"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Python: Remote Attach"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"type"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"python"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"request"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"attach"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"port"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5678")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"host"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"localhost"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"pathMappings"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"localRoot"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"${workspaceFolder}/api/data/opa"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"remoteRoot"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/data/opa"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"better-exceptions"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#better-exceptions"}},[t._v("#")]),t._v(" Better exceptions")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/Qix-/better-exceptions",target:"_blank",rel:"noopener noreferrer"}},[t._v("github"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("Get better exception output on the console. It does many things, but one very usefull thing is that it shows you the content of variables inside the different exception frames.")]),t._v(" "),a("h2",{attrs:{id:"plugins"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#plugins"}},[t._v("#")]),t._v(" Plugins")]),t._v(" "),a("p",[t._v("The plugin-system is powerfull, you can read about it "),a("a",{attrs:{href:"./api/plugin-system"}},[t._v("here")]),t._v(".\nSince there is a lot to it for developing them, there is a "),a("a",{attrs:{href:"./plugins"}},[t._v("dedicated")]),t._v(" page for info about it.")]),t._v(" "),a("h2",{attrs:{id:"interactive-shell"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#interactive-shell"}},[t._v("#")]),t._v(" Interactive shell")]),t._v(" "),a("p",[t._v("You can get an interactive shell in the context of opa-stack by using "),a("code",[t._v("opa/scripts/shell.sh")]),t._v(". It will start an ipython shell where the plugins are available.")]),t._v(" "),a("p",[t._v("Example (from a host-machine using docker-compose)")]),t._v(" "),a("div",{staticClass:"language-python extra-class"},[a("pre",{pre:!0,attrs:{class:"language-python"}},[a("code",[t._v("docker"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("compose "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("exec")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("w "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("data api "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("opa"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("scripts"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shell"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sh\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_instance\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus\nOut"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" Database"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("ConnectionPool"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("Connection"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("host"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("redis"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("port"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6379")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("db"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">>")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'test'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("set")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'test'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nOut"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v("\n\nIn "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'test'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nOut"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("b'1'")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/11.b8f9ff69.js b/assets/js/11.b8f9ff69.js new file mode 100644 index 0000000..fe35e54 --- /dev/null +++ b/assets/js/11.b8f9ff69.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{221:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"examples"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#examples"}},[t._v("#")]),t._v(" Examples")]),t._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("Read "),a("a",{attrs:{href:"./new-project"}},[t._v("this")]),t._v(" first if you need a new project")])]),t._v(" "),a("p",[t._v("Note that on all the examples, you will also get these url's")]),t._v(" "),a("ul",[a("li",[t._v("http://127.0.0.1:8001/docs - Swagger documentation")]),t._v(" "),a("li",[t._v("http://127.0.0.1:8001/redoc - Redoc documentation")]),t._v(" "),a("li",[t._v("http://127.0.0.1:8001/openapi.json - OpenAPI schema")])]),t._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("The "),a("a",{attrs:{href:"https://fastapi.tiangolo.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FastAPI"),a("OutboundLink")],1),t._v(" have already made an extremely usefull documentation that you should consider reading as well.\nBecause it exists, this doc won't be about deep things you can do with FastAPI")])]),t._v(" "),a("h2",{attrs:{id:"hello-world"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#hello-world"}},[t._v("#")]),t._v(" Hello world")]),t._v(" "),a("p",[t._v("The first example is a simple docker-compose setup where you will")]),t._v(" "),a("ul",[a("li",[t._v("Setup\n"),a("ul",[a("li",[a("p",[t._v("Uses docker-compose")])]),t._v(" "),a("li",[a("p",[t._v("Use only the api container")])]),t._v(" "),a("li",[a("p",[t._v("Expose output from a simple command on "),a("code",[t._v("/hello")])])]),t._v(" "),a("li",[a("p",[t._v("Mount the plugins from a local plugin-folder.")])]),t._v(" "),a("li",[a("p",[t._v("Things to try")]),t._v(" "),a("ul",[a("li",[t._v("http://127.0.0.1:8001/hello - The output of the route in "),a("code",[t._v("hello.py")])])])])])])]),t._v(" "),a("h3",{attrs:{id:"files"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#files"}},[t._v("#")]),t._v(" Files")]),t._v(" "),a("ul",[a("li",[t._v("Files at github: "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/hello-world",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/hello-world"),a("OutboundLink")],1)])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("version")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3.7"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("api")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" opastack/api"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("latest\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:8001:8000"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("volumes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" ./plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("/plugins\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/hello.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_router\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/hello"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("return_string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Hello to you'")]),t._v("\n")])])])]),t._v(" "),a("h2",{attrs:{id:"timekeeper"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#timekeeper"}},[t._v("#")]),t._v(" Timekeeper")]),t._v(" "),a("p",[t._v("This example is still simple, but shows you how to do a little bit more, including a better development environment.")]),t._v(" "),a("ul",[a("li",[a("p",[t._v("Setup")]),t._v(" "),a("ul",[a("li",[t._v("Uses docker-compose")]),t._v(" "),a("li",[t._v("Using only the api container")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/time")]),t._v(" that also accept some parameters")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/month/{month}")]),t._v(" that converts a number (1-12) to month-name. Crashes if it is not able to...")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/sleep-async/{seconds}")]),t._v(" that sleeps.. This is an async function")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/sleep-sync/{seconds}")]),t._v(" that sleeps.. This is not an async function..")]),t._v(" "),a("li",[t._v("Mount the plugins from a local plugin-folder, watches them for changes.")]),t._v(" "),a("li",[t._v("Sets the environment to "),a("code",[t._v("DEV")]),t._v(" (see "),a("RouterLink",{attrs:{to:"/guide/api/configuration.html#dev"}},[t._v("docs")]),t._v(" for more info)\n"),a("ul",[a("li",[t._v("In addition to other things, enabling "),a("code",[t._v("DEV")]),t._v(" gives some neat developing features.. Check "),a("RouterLink",{attrs:{to:"/guide/development.html#development-mode-env-dev"}},[t._v("here")]),t._v(" for more info how to leverage them")],1)])],1)])]),t._v(" "),a("li",[a("p",[t._v("Things to try")]),t._v(" "),a("ul",[a("li",[t._v("http://127.0.0.1:8001/time - Should show you the time in the default format")]),t._v(" "),a("li",[t._v("http://127.0.0.1:8001/docs#/timekeeper/get_time_time_get > Try it out > change format > Execute")]),t._v(" "),a("li",[t._v("http://127.0.0.1:8001/month/2")]),t._v(" "),a("li",[t._v("http://127.0.0.1:8001/month/20\n"),a("ul",[a("li",[t._v("See the better-exception (with variable output) in the console.")]),t._v(" "),a("li",[t._v("Try setting up vscode "),a("RouterLink",{attrs:{to:"/guide/development.html#PTVSD"}},[t._v("debugging")]),t._v(" and set a breakpoint and so on..")],1)])]),t._v(" "),a("li",[t._v("Hammer on http://127.0.0.1:8001/sleep-async/10 and http://127.0.0.1:8001/sleep-sync/20 on multiple terminals.. See how async is the king when it comes to things that blocks.")]),t._v(" "),a("li",[t._v("Or use something like "),a("a",{attrs:{href:"https://github.com/rakyll/hey",target:"_blank",rel:"noopener noreferrer"}},[t._v("hey"),a("OutboundLink")],1),t._v(", for better benchmarking.\n"),a("ul",[a("li",[t._v("hey -n 100 -c 100 http://127.0.0.1:8001/sleep-async/5")]),t._v(" "),a("li",[t._v("hey -n 100 -c 100 http://127.0.0.1:8001/sleep-sync/5")])])])])])]),t._v(" "),a("h3",{attrs:{id:"files-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#files-2"}},[t._v("#")]),t._v(" Files")]),t._v(" "),a("ul",[a("li",[t._v("Files at github: "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/timekeeper",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/timekeeper"),a("OutboundLink")],1)])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("version")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3.7"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("api")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" opastack/api"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("latest\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:8001:8000"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:5678:5678"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"@merge /plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_BLACKLIST_LIST")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'[\"/data/opa/plugins/core-selfhosted\"]'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ENV")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"dev"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_LOGLEVEL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("volumes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" ./plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("/plugins\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/timekeeper.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" logging\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" datetime\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" secrets\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" time "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" sleep\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" asyncio "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" sleep "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" async_sleep\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_router\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Using tags helps separate your items from the also included demo-plugins")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/time"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tags"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"timekeeper"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get_time")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'%Y-%m-%d %H:%M:%S'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" datetime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("datetime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("now"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("strftime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("format")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/month/{month}"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tags"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"timekeeper"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get_monthname")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("month"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n Make sure you give me a valid month number, or I might crash...\n """')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" datetime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("datetime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("strptime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("month"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'%m'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("strftime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'%B'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/sleep-async/{seconds}"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tags"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"timekeeper"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("async_sleeper")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n I sleep for some seconds, then return how long I slept.. Neat!\n But I\'m also async, so it shoulnt block anything..\n """')]),t._v("\n randstring "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" secrets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("token_urlsafe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n logging"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Start async sleep for (")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("randstring"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(") for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" async_sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n logging"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Ending async sleep for (")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("randstring"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(") for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'I slept for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(" seconds'")])]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/sleep-sync/{seconds}"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tags"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"timekeeper"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sync_sleeper")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token triple-quoted-string string"}},[t._v('"""\n I\'m almost like sleep-async, but I\'m not async.. That means that I block python when I do nothing.\n """')]),t._v("\n randstring "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" secrets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("token_urlsafe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n logging"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Start sync sleep for (")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("randstring"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(") for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n logging"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Ending sync sleep for (")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("randstring"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(") for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'I slept for ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("seconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(" seconds'")])]),t._v("\n")])])])]),t._v(" "),a("h2",{attrs:{id:"redis"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#redis"}},[t._v("#")]),t._v(" Redis")]),t._v(" "),a("p",[t._v("Example using redis (both async and normal), using two different libs (aioredis and walrus), see "),a("RouterLink",{attrs:{to:"/guide/optional-components-reference.html#redis"}},[t._v("dependencies")]),t._v(" for more info.")],1),t._v(" "),a("ul",[a("li",[a("p",[t._v("Setup")]),t._v(" "),a("ul",[a("li",[t._v("Uses docker-compose")]),t._v(" "),a("li",[t._v("Using the api container, and a redis-container")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/counter-async")]),t._v(" increments when you visit using aioredis")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/counter-sync")]),t._v(" increments when you visit using walrus")]),t._v(" "),a("li",[t._v("Expose output to GET and POST on "),a("code",[t._v("/bloom")]),t._v(", one to add entries to a bloom-filter (using walrus) and one to check. Walrus have a ton of neat feature, this is one of them.")])])]),t._v(" "),a("li",[a("p",[t._v("Things to try")]),t._v(" "),a("ul",[a("li",[t._v("http://127.0.0.1:8001/counter-async - See a counter")]),t._v(" "),a("li",[a("code",[t._v('cat a_file.md | curl -X POST -d @- "http://localhost:8000/bloom"')]),t._v(" - fill up a bloom filter")]),t._v(" "),a("li",[t._v("http://localhost:8000/bloom?string=sleep - Check for "),a("code",[t._v("sleep")]),t._v(" in the bloomfilter")])])])]),t._v(" "),a("h3",{attrs:{id:"files-3"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#files-3"}},[t._v("#")]),t._v(" Files")]),t._v(" "),a("ul",[a("li",[t._v("Files at github: "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/redis",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/redis"),a("OutboundLink")],1)])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("version")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3.7"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("api")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# image: opastack/api:latest")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 25724b069f78\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:8001:8000"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("volumes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" ./plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("/plugins\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("redis")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis:5"')]),t._v("\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/timekeeper.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" get_router\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/counter-async"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("counter_async")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("None")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n counter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'aioredis'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("incr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("or")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'incr-async'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Counter is ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("counter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/counter-sync"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("counter_sync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("None")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n counter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("incr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("or")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'incr-sync'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Counter is ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("counter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/bloom"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("check_bloom_filter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("string"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n bf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bloom_filter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'bf'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" string "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" bf\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/bloom"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("add_bloom_filter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("string"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Waiting for https://github.com/tiangolo/fastapi/issues/1018 to have plain/text input")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Possible now, but not with generation of the openapi spec..")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n bf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("bloom_filter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'bf'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" string"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("split"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("' '")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n bf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("add"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Added entries'")])]),t._v("\n")])])])]),t._v(" "),a("h2",{attrs:{id:"background-tasks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#background-tasks"}},[t._v("#")]),t._v(" Background tasks")]),t._v(" "),a("p",[t._v("Background tasks are built into FastAPI/Starlette. If you come from the sync-world, you are probably used to Celery.\nCelery complicates a lot, so if you just got a simple fire-and-forget job. A background-task might fit your needs.")]),t._v(" "),a("p",[t._v("See "),a("a",{attrs:{href:"#celery-task"}},[t._v("below")]),t._v(" for info about running a celery task.")]),t._v(" "),a("ul",[a("li",[a("p",[t._v("Setup")]),t._v(" "),a("ul",[a("li",[t._v("Uses docker-compose")]),t._v(" "),a("li",[t._v("Using the api container")]),t._v(" "),a("li",[t._v("Uses redis for locking and keeping track of a string (not needed for running tasks where you don't care about results)")]),t._v(" "),a("li",[t._v("Runs background tasks using "),a("a",{attrs:{href:"https://fastapi.tiangolo.com/tutorial/background-tasks/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FastAPI"),a("OutboundLink")],1),t._v("'s "),a("a",{attrs:{href:"https://www.starlette.io/background/",target:"_blank",rel:"noopener noreferrer"}},[t._v("starlette background tasks"),a("OutboundLink")],1)]),t._v(" "),a("li",[t._v("Let you POST to "),a("code",[t._v("/runone")]),t._v(" to put a task to the queue, it runs for 4 seconds")]),t._v(" "),a("li",[t._v("Expose output on "),a("code",[t._v("/runone")]),t._v(" to see status of which task is currently running")])])]),t._v(" "),a("li",[a("p",[t._v("Things to try")]),t._v(" "),a("ul",[a("li",[t._v("Terminal 1 - Status\n"),a("ul",[a("li",[a("code",[t._v("while true; do curl http://localhost:8001/runone; echo; sleep 1; done")])])])]),t._v(" "),a("li",[t._v("Terminal 2 - Trigger new tasks, try triggering many, see they queue up on terminal 1\n"),a("ul",[a("li",[t._v("curl -X POST http://localhost:8001/runone")])])])])])]),t._v(" "),a("h3",{attrs:{id:"files-4"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#files-4"}},[t._v("#")]),t._v(" Files")]),t._v(" "),a("ul",[a("li",[t._v("Files at github: "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/background-task",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/background-task"),a("OutboundLink")],1)])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("version")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3.7"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("api")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" opastack/api"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("latest\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:8001:8000"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("volumes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" ./plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("/plugins\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("redis")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis:5"')]),t._v("\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/tasks.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" time "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" sleep\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" secrets "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" token_urlsafe\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" fastapi "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" BackgroundTasks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" APIRouter\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" get_instance\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("queuer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n lock "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lock"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'runone'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("with")]),t._v(" lock"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("set")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'runone'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" text"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("delete"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'runone'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/runone"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("runone_post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("background_tasks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" BackgroundTasks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n random_str "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" token_urlsafe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n background_tasks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("add_task"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("queuer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" random_str"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"message"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('f"Triggered background task: ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("random_str"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/runone"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("runone_get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"current_task"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'runone'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])]),t._v(" "),a("h2",{attrs:{id:"celery-task"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#celery-task"}},[t._v("#")]),t._v(" Celery task")]),t._v(" "),a("p",[a("a",{attrs:{href:"http://www.celeryproject.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Celery"),a("OutboundLink")],1),t._v(" is a very powerfull distributed task-queue. It is much more feature-rich than the default background tasks available in FastAPI/Starlette.")]),t._v(" "),a("ul",[a("li",[a("p",[t._v("Setup")]),t._v(" "),a("ul",[a("li",[t._v("Uses docker-compose")]),t._v(" "),a("li",[t._v("Same container image, but different config for\n"),a("ul",[a("li",[t._v("api container")]),t._v(" "),a("li",[t._v("worker-math - Runs celery and consumes the "),a("code",[t._v("math")]),t._v(" queue")]),t._v(" "),a("li",[t._v("worker-counter - Runs celery and consumes the "),a("code",[t._v("counter")]),t._v(" queue")])])]),t._v(" "),a("li",[t._v("Redis for storing task-results")]),t._v(" "),a("li",[t._v("Rabbitmq for keeping track of tasks (broker)")]),t._v(" "),a("li",[t._v("Flower to see statuses")])])]),t._v(" "),a("li",[a("p",[t._v("Things to try")]),t._v(" "),a("ul",[a("li",[t._v("http://localhost:8001/inc/5 - Hit the url a couple of times\n"),a("ul",[a("li",[t._v("They will queue up. At the end of each task, the celery worker (in the "),a("code",[t._v("worker-counter")]),t._v(" container) will increment a counter")]),t._v(" "),a("li",[t._v("The same counter is reported as output when accessing this url, since the api can access the same redis instance as the worker")])])]),t._v(" "),a("li",[t._v("http://localhost:8001/div/A/B - Hit the other worker ("),a("code",[t._v("worker-math")]),t._v(")\n"),a("ul",[a("li",[t._v("http://localhost:8001/div/2/1 - normal queuing")]),t._v(" "),a("li",[t._v("http://localhost:8001/div/2/0 - one that will get an error")])])]),t._v(" "),a("li",[t._v("See tasks as they arrive\n"),a("ul",[a("li",[t._v("Using celery events (doesnt mather which)\n"),a("ul",[a("li",[t._v("docker-compose exec -w /data worker-counter celery -A opa.main events")]),t._v(" "),a("li",[t._v("docker-compose exec -w /data worker-math celery -A opa.main events")])])]),t._v(" "),a("li",[t._v("Using flower\n"),a("ul",[a("li",[t._v("http://localhost:5555/")])])])])]),t._v(" "),a("li",[t._v("http://localhost:8001/inc/100 - Queue a task which takes 100 seconds\n"),a("ul",[a("li",[t._v("Take a note of the task_id in the output")])])]),t._v(" "),a("li",[t._v("http://localhost:8001/status/27deec1a-c8f0-4d0d-95dc-59c6dd937207 - Check status for task\n"),a("ul",[a("li",[t._v("The inc task updates some meta-info that we can query using another endpoint")]),t._v(" "),a("li",[t._v("There are also a background task that will get those updates in the fastapi context, see the "),a("code",[t._v("__init__.py")]),t._v(" file for info")])])]),t._v(" "),a("li",[t._v("http://localhost:8001/last_status - Another way to get the status")])])]),t._v(" "),a("li",[a("p",[t._v("Files at github: "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/celery-task",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/celery-task"),a("OutboundLink")],1)])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("version")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3.7"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("services")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("api")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" opastack/api"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("latest\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"127.0.0.1:8001:8000"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("environment")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPA_PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("volumes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" ./plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("/plugins\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("redis")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis:5"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("rabbitmq")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"rabbitmq:3.8-management"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"15672:15672"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Management port")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("flower")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"mher/flower:latest"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("command")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-A proj --broker=amqp://guest:guest@rabbitmq:5672//"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ports")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"5555:5555"')]),t._v("\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/celerydemo/__init__.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" app\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("celery_config")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'driver.celery.setup'")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" celery_app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" task_candidates"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n celery_app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("conf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("task_routes "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"worker.celery_worker.test_celery"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test-queue"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n celery_app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("conf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("update"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("task_track_started"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n celery_app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("autodiscover_tasks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("task_candidates"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" celery_app\n\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/add/{num1}/{num2}"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("root")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("num1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" num2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" celerydemo"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("tasks "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" test_celery\n\n celery "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'celery'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n test_celery"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("delay"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'abc'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n count "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'celery'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"message"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Word received"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'count'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" count"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("plugins/celerydemo/tasks.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" time "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" sleep\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" celery "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" current_task\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_instance\n\ncelery "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'celery'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@celery"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("task")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("test_celery")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("word"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("range")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n sleep"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n current_task"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("update_state"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'PROGRESS'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n meta"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'process_percent'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("incr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'celery'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v('f"test task return ')]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("word"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('...."')])])])])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/12.3a8e2acb.js b/assets/js/12.3a8e2acb.js new file mode 100644 index 0000000..2bf0888 --- /dev/null +++ b/assets/js/12.3a8e2acb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{222:function(e,t,r){"use strict";r.r(t);var o=r(0),s=Object(o.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("h1",{attrs:{id:"new-project"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#new-project"}},[e._v("#")]),e._v(" New project")]),e._v(" "),r("p",[e._v("The only sane way to run the opa-stack is using containers and it is build using containers in mind from the very beginning.\nThere are multiple ways to run it, so choose whatever orchestrator fits you best below.")]),e._v(" "),r("p",[e._v("There are also some "),r("a",{attrs:{href:"./optional-components-reference"}},[e._v("optional components")]),e._v(" you can use together with opa-stack if you want caching, db or and so on.")]),e._v(" "),r("h2",{attrs:{id:"orchestrators"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#orchestrators"}},[e._v("#")]),e._v(" Orchestrators")]),e._v(" "),r("h3",{attrs:{id:"docker-compose"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#docker-compose"}},[e._v("#")]),e._v(" docker-compose")]),e._v(" "),r("p",[e._v("Docker-compose is the simplest of the container orchestrators. You can download it from "),r("a",{attrs:{href:"https://docs.docker.com/compose/install/",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),r("OutboundLink")],1),e._v(", and read the getting started guide "),r("a",{attrs:{href:"https://docs.docker.com/compose/gettingstarted/",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),r("OutboundLink")],1),e._v(".")]),e._v(" "),r("p",[e._v("Take a look at the simplest example project "),r("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/hello-world",target:"_blank",rel:"noopener noreferrer"}},[e._v("here"),r("OutboundLink")],1),e._v(".\nIn the folder just run "),r("code",[e._v("docker-compose up")]),e._v(", and you got things running.. See the "),r("a",{attrs:{href:"https://docs.docker.com/compose/",target:"_blank",rel:"noopener noreferrer"}},[e._v("docker-compose docs"),r("OutboundLink")],1),e._v(" for more info.")]),e._v(" "),r("h3",{attrs:{id:"kubernetes-helm"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#kubernetes-helm"}},[e._v("#")]),e._v(" kubernetes (helm)")]),e._v(" "),r("p",[e._v("Nothing here yet...")])])}),[],!1,null,null,null);t.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/13.e8dd8de7.js b/assets/js/13.e8dd8de7.js new file mode 100644 index 0000000..b22532b --- /dev/null +++ b/assets/js/13.e8dd8de7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{223:function(t,s,a){"use strict";a.r(s);var e=a(0),n=Object(e.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"optional-components-reference"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#optional-components-reference"}},[t._v("#")]),t._v(" Optional-components reference")]),t._v(" "),a("p",[t._v("If you havent read it, you should read about "),a("a",{attrs:{href:"./plugins"}},[t._v("plugins")]),t._v(" first")]),t._v(" "),a("h2",{attrs:{id:"configuration"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#configuration"}},[t._v("#")]),t._v(" Configuration")]),t._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("You must use dynaconf merge when changing these parameters.. Example")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("myenv")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTIONAL_COMPONENTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("dynaconf_merge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("WALRUS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-walrus"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis"')]),t._v("\n")])])])]),t._v(" "),a("p",[t._v("Take a look at the default configuration "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/blob/master/api/data/opa/default-settings.yaml",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(" or see the file below (it is under the "),a("code",[t._v("OPTIONAL_COMPONENTS")]),t._v(" key) for some examples.")]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("default-settings.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MODE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"prod"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_NAME")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"opa-stack"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_DESCRIPTION")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_VERSION")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.1.0"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Urls to automatic documentation. Set to null to disable")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DOCS_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/docs"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("REDOC_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/redoc"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPENAPI_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/openapi.json"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Can only be null if DOCS_URL and REDOC_URL is also null")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPENAPI_PREFIX")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_LIST")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_RE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_TAGS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_LIST")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_RE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_TAGS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# CORS")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_ORIGINS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_CREDENTIALS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_METHODS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_HEADERS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("SECRET_KEY")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Configuration for optional components..")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# They all have a helper-file in ../utils/{component_name.lower()}.py, so check")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# in them if you wonder how these values are used. They are also listed in the docs")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# @ https://opa-stack.github.io/guide/components.html")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# The LOAD option is always present, and can be one of auto|yes|no, all defaulting to 'auto'.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * auto: Makes opa-stack look for the component using a simple dns-check or other simple checks")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('# Ie.. Check if we "should" be able to use this component.')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('# It will then try to connect as if you had written "yes", ie, it fails if it is not able to..')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * yes: Makes the coponent required")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * no: Makes it not check at all.")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTIONAL_COMPONENTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MONGODB")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"mongodb-async-motor"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"mongodb://mongo:mongo@mongo:27017/opa"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("WALRUS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-walrus"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("AIOREDIS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-aioredis"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("CELERY")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"celery"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BACKEND_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis/1"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BROKER_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"pyamqp://guest@rabbitmq//"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Used different places, currently:")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * Setting FastAPI/Starlette debug-mode (https://www.starlette.io/applications/)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DEBUG")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Turns on better exceptions, ie, prettier, and with some more info (like variables)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Python Tools for Visual Studio debug server, running on port 5678")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PTVSD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# External js files are loaded from the internet as default")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("SELF_HOSTED")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("dev")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MODE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"dev"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PTVSD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS_MAX_LENGTH")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/data/opa/demo-plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" dynaconf_merge\n")])])])]),t._v(" "),a("h2",{attrs:{id:"usage"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#usage"}},[t._v("#")]),t._v(" Usage")]),t._v(" "),a("p",[t._v("When you want to use an optional-component you the best way is to use "),a("code",[t._v("opa.get_instance(name)")]),t._v(" like this")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" get_router\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("plugins"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("driver_redis "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Walrus\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("counter_sync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n walrus "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_intance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'walrus'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n counter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" walrus"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("incr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'counter'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Counter is ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("counter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),t._v("\n")])])]),a("div",{staticClass:"custom-block warning"},[a("p",{staticClass:"custom-block-title"},[t._v("WARNING")]),t._v(" "),a("p",[a("code",[t._v("opa.get_instance()")]),t._v(" takes the lowercase component key-name as input")])]),t._v(" "),a("p",[t._v("The other ways to get the instance (might be usefull in some cases)")]),t._v(" "),a("ul",[a("li",[t._v("Use "),a("code",[t._v("component = opa.get_component(name)")]),t._v(" "),a("ul",[a("li",[t._v("The instance will be under "),a("code",[t._v("component.instance")]),t._v(", but there are other component-info you might want under "),a("code",[t._v("component")])])])]),t._v(" "),a("li",[t._v("Use "),a("code",[t._v("pm = opa.get_plugin_manager()")]),t._v(", then "),a("code",[t._v("pm.optional_components[name]")]),t._v(" to get the component instance")])]),t._v(" "),a("h2",{attrs:{id:"drivers"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#drivers"}},[t._v("#")]),t._v(" Drivers")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("Backend")]),t._v(" "),a("th",[t._v("Library")]),t._v(" "),a("th",[t._v("Drivername")]),t._v(" "),a("th",[t._v("Async")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[t._v("Redis")]),t._v(" "),a("td",[a("a",{attrs:{href:"https://aioredis.readthedocs.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Aioredis"),a("OutboundLink")],1)]),t._v(" "),a("td",[a("a",{attrs:{href:"#aioredis"}},[t._v("redis-aioredis")])]),t._v(" "),a("td",[t._v("yes")])]),t._v(" "),a("tr",[a("td",[t._v("Redis")]),t._v(" "),a("td",[a("a",{attrs:{href:"https://walrus.readthedocs.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("Walrus"),a("OutboundLink")],1)]),t._v(" "),a("td",[a("a",{attrs:{href:"#walrus"}},[t._v("redis-walrus")])]),t._v(" "),a("td",[t._v("no")])]),t._v(" "),a("tr",[a("td",[t._v("Mongodb")]),t._v(" "),a("td",[a("a",{attrs:{href:"https://motor.readthedocs.io/en/stable/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Motor"),a("OutboundLink")],1)]),t._v(" "),a("td",[a("a",{attrs:{href:"#motor"}},[t._v("mongodb-async-motor")])]),t._v(" "),a("td",[t._v("yes")])]),t._v(" "),a("tr",[a("td",[t._v("Celery")]),t._v(" "),a("td",[a("a",{attrs:{href:"http://www.celeryproject.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Celery"),a("OutboundLink")],1)]),t._v(" "),a("td",[a("a",{attrs:{href:"#celery"}},[t._v("celery")])]),t._v(" "),a("td",[t._v("no")])])])]),t._v(" "),a("h3",{attrs:{id:"redis"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#redis"}},[t._v("#")]),t._v(" Redis")]),t._v(" "),a("h4",{attrs:{id:"aioredis"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#aioredis"}},[t._v("#")]),t._v(" Aioredis")]),t._v(" "),a("h4",{attrs:{id:"walrus"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#walrus"}},[t._v("#")]),t._v(" Walrus")]),t._v(" "),a("ul",[a("li",[t._v("Very powerful redis library with support for a lot of additional features")])]),t._v(" "),a("h3",{attrs:{id:"mongodb"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#mongodb"}},[t._v("#")]),t._v(" Mongodb")]),t._v(" "),a("h4",{attrs:{id:"motor"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#motor"}},[t._v("#")]),t._v(" Motor")]),t._v(" "),a("h3",{attrs:{id:"celery-tasks"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#celery-tasks"}},[t._v("#")]),t._v(" Celery / tasks")]),t._v(" "),a("p",[t._v("If you need basic background tasks, look into "),a("a",{attrs:{href:"https://fastapi.tiangolo.com/tutorial/background-tasks/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FastAPI/Starlette BackgroundTasks"),a("OutboundLink")],1),t._v(" instead.")]),t._v(" "),a("p",[t._v("There is builtin support for running celery tasks, see the "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/celery-task",target:"_blank",rel:"noopener noreferrer"}},[t._v("sample-project"),a("OutboundLink")],1),t._v(" for details, view info about the sample project "),a("RouterLink",{attrs:{to:"/guide/examples.html#celery-task"}},[t._v("here")])],1),t._v(" "),a("p",[t._v("The opa-stack's api-container can run in "),a("code",[t._v("worker mode")]),t._v(", ie, it will run a celery worker with the same codebase as the rest of the api. This is by far the simplest setup, but feel free to run your own workers if you need.")]),t._v(" "),a("ul",[a("li",[t._v("Set environment-variable "),a("code",[t._v("MODE=worker")]),t._v(" to start it as a worker")]),t._v(" "),a("li",[t._v("Set additional celery params ("),a("code",[t._v("celery worker -A opa.main ${CELERY_PARAMS}")]),t._v(")")])]),t._v(" "),a("p",[t._v("Some important notes about using celery in opa-stack is:")]),t._v(" "),a("ul",[a("li",[t._v("The tasks must be defined in "),a("code",[t._v("tasks.py")]),t._v(", ie, you will need your plugin to be a python package (folder with "),a("code",[t._v("__init__.py")]),t._v(").")]),t._v(" "),a("li",[t._v("You can access optional-components as usual in the tasks, but only non-async drivers are working. Ie, you can't use drivers like "),a("code",[t._v("motor")]),t._v(" or "),a("code",[t._v("aioredis")]),t._v(" in your tasks.\n"),a("ul",[a("li",[t._v("It is important that you DONT import any of your tasks on top of the file where your route is defined. Ie, do the "),a("code",[t._v("from packagename.tasks import sometask")]),t._v(" inside your "),a("code",[t._v("@router.get('/')...")]),t._v(" function!")])])]),t._v(" "),a("li",[t._v("The celery worker is using most of the opa-stack code. So configuration, driver-loading, hooks and so on will also work in the celery worker.")])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/14.5b8196d5.js b/assets/js/14.5b8196d5.js new file mode 100644 index 0000000..ecd681f --- /dev/null +++ b/assets/js/14.5b8196d5.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{224:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"plugins"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#plugins"}},[t._v("#")]),t._v(" Plugins")]),t._v(" "),a("p",[t._v("A "),a("code",[t._v("plugin")]),t._v(" is how you make things using opa-stack.\nThey are just python packages or modules available to opa-stack. See "),a("a",{attrs:{href:"./api/plugin-system"}},[t._v("general info")]),t._v(" about the plugin-system to see how it works.")]),t._v(" "),a("h2",{attrs:{id:"types-of-plugins"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#types-of-plugins"}},[t._v("#")]),t._v(" Types of plugins")]),t._v(" "),a("p",[t._v("Different types of plugins are initialized in different order. Example, all hooks are initialized before drivers are initialized. Drivers are done when setup is run.")]),t._v(" "),a("h3",{attrs:{id:"importables"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#importables"}},[t._v("#")]),t._v(" Importables")]),t._v(" "),a("p",[t._v("Every "),a("code",[t._v("PLUGIN_PATHS")]),t._v(" are loaded into pythons "),a("code",[t._v("sys.path")]),t._v(" and imported. Therefor, you can also have plugins that exposes functionality to other plugins.")]),t._v(" "),a("p",[t._v("So, if you got")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("/plugins/common/math_utils.py")]),t._v(" with a function "),a("code",[t._v("double()")])]),t._v(" "),a("li",[a("code",[t._v("/plugins/secure/grade_calculations.py")]),t._v(" that adds a route. It can use "),a("code",[t._v("from math_utils import double")]),t._v(" without problems")])]),t._v(" "),a("h3",{attrs:{id:"hook-definition"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#hook-definition"}},[t._v("#")]),t._v(" Hook Definition")]),t._v(" "),a("p",[t._v("Hooks need to be defined before you can use them, so for this, you need a hook-definition.")]),t._v(" "),a("p",[t._v("A simple example looks like")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("core"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("plugin "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" HookDefinition\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("name_hook")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("HookDefinition"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n required "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("True")]),t._v("\n is_async "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'fullname'")]),t._v("\n")])])]),a("p",[t._v("Parameters:")]),t._v(" "),a("ul",[a("li",[t._v("required (default: False): Set to True if you don't want the application to be able to start unless there is a hook for this definition.")]),t._v(" "),a("li",[t._v("is_async (default: False): Set to True if the hook should be an async function.")]),t._v(" "),a("li",[t._v("name (default: function-name): Override the function-name as the name of the hook.")])]),t._v(" "),a("h3",{attrs:{id:"hook"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#hook"}},[t._v("#")]),t._v(" Hook")]),t._v(" "),a("p",[t._v("Hooks are used if you want to replace or provide a custom function for a part of your code.")]),t._v(" "),a("p",[t._v("Example, think of a tiny generic app like this:")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("core"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("plugin "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n get_plugin_manager"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n call_hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Hook\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/get-fullname/{firstname}/{surname}"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("show_name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n firstname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" surname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("str")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n fullname "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" call_hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'fullname'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" firstname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" surname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'name'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" fullname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("It calculates "),a("code",[t._v("fullname")]),t._v(" based on whatever the hook called "),a("code",[t._v("fullname")]),t._v(" returns.\nThe "),a("code",[t._v("fullname")]),t._v(" is already defined above in "),a("code",[t._v("HookDefinition")]),t._v(", so now lets define the function itself in a completly different plugin.")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("core"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("plugin "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Hook\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("fullname_hook")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Hook"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'fullname'")]),t._v("\n order "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("run")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" firstname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" lastname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("firstname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(" ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("lastname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),t._v("\n")])])]),a("p",[t._v("Some key takeaways:")]),t._v(" "),a("ul",[a("li",[t._v("If the hook-definition's "),a("code",[t._v("is_async")]),t._v(" is True, you will get an error if the run function is not async ("),a("code",[t._v("async def run(...)")]),t._v(")")]),t._v(" "),a("li",[a("code",[t._v("call_hook")]),t._v(" is for sync, there is also a "),a("code",[t._v("call_hook_async")]),t._v(" for async")]),t._v(" "),a("li",[t._v("Whatever arguments that the call-functions gets are what the run functions will get.")]),t._v(" "),a("li",[t._v("If there are multiple hooks for a single definition, the one with the highest "),a("code",[t._v("order")]),t._v(" wins (default order is 0).")]),t._v(" "),a("li",[t._v("The "),a("code",[t._v("HookDefinition")]),t._v(" and the route that calls "),a("code",[t._v("call_hook")]),t._v(" are usually together in the same plugin. The hooks are normally separate plugins.")]),t._v(" "),a("li",[t._v("A typical usecase is to also define a "),a("code",[t._v("Hook")]),t._v(" with default order of "),a("code",[t._v("-1")]),t._v(" togehter with the "),a("code",[t._v("HookDefinition")]),t._v(", that way, your pm.call will have a default hook")]),t._v(" "),a("li",[t._v("There can only be 1 hook definition per "),a("code",[t._v("name")]),t._v(" or you will get an error.")])]),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/hooks",target:"_blank",rel:"noopener noreferrer"}},[t._v("Complete example"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("Now, this was a really simple example. But it can be used for many things")]),t._v(" "),a("ul",[a("li",[t._v("Language or environment specific code (load different hook-plugins based on tags)")]),t._v(" "),a("li",[t._v("A hierarcy of internal plugins, where you define some of their behavior dynamicly using a simple environment variable to choose an env")]),t._v(" "),a("li",[t._v("Provide additional info if running in dev-env")]),t._v(" "),a("li",[t._v("Provide a solid base, for others to built small custom plugins on")])]),t._v(" "),a("h3",{attrs:{id:"drivers-optional-components"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#drivers-optional-components"}},[t._v("#")]),t._v(" Drivers / Optional-components")]),t._v(" "),a("p",[t._v("More than often, you will need a database, cache backend (redis) or another 3rd party component.\nYou can use one of the "),a("a",{attrs:{href:"./optional-components-reference"}},[t._v("built-in drivers")]),t._v(", or you can make your own very easiely.")]),t._v(" "),a("h4",{attrs:{id:"configuration"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#configuration"}},[t._v("#")]),t._v(" Configuration")]),t._v(" "),a("p",[t._v("The default configuration loads a couple of drivers if it can find the component. That means that, if opa-stack think redis is available, it will try to connect to it. The redis drivers just uses a simple host-lookup for this check (check if "),a("code",[t._v("redis")]),t._v(" avalable).")]),t._v(" "),a("p",[t._v("All drivers are configured under the "),a("code",[t._v("OPTIONAL_COMPONENTS")]),t._v(" in the configuration. The default configuration is "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/blob/master/api/data/opa/default-settings.yaml",target:"_blank",rel:"noopener noreferrer"}},[t._v("here"),a("OutboundLink")],1),t._v(", or see below.")]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("docker-compose.yaml")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MODE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"prod"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_NAME")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"opa-stack"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_DESCRIPTION")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PROJECT_VERSION")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0.1.0"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Urls to automatic documentation. Set to null to disable")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DOCS_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/docs"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("REDOC_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/redoc"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPENAPI_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/openapi.json"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Can only be null if DOCS_URL and REDOC_URL is also null")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPENAPI_PREFIX")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_LIST")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_RE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_WHITELIST_TAGS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_LIST")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_RE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_BLACKLIST_TAGS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# CORS")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_ORIGINS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_CREDENTIALS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_METHODS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("ALLOW_HEADERS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"*"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("SECRET_KEY")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Configuration for optional components..")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# They all have a helper-file in ../utils/{component_name.lower()}.py, so check")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# in them if you wonder how these values are used. They are also listed in the docs")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# @ https://opa-stack.github.io/guide/components.html")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# The LOAD option is always present, and can be one of auto|yes|no, all defaulting to 'auto'.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * auto: Makes opa-stack look for the component using a simple dns-check or other simple checks")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('# Ie.. Check if we "should" be able to use this component.')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('# It will then try to connect as if you had written "yes", ie, it fails if it is not able to..')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * yes: Makes the coponent required")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * no: Makes it not check at all.")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTIONAL_COMPONENTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MONGODB")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"mongodb-async-motor"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"mongodb://mongo:mongo@mongo:27017/opa"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("WALRUS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-walrus"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("AIOREDIS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-aioredis"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("CELERY")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"celery"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BACKEND_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://redis/1"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BROKER_URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"pyamqp://guest@rabbitmq//"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Used different places, currently:")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# * Setting FastAPI/Starlette debug-mode (https://www.starlette.io/applications/)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DEBUG")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Turns on better exceptions, ie, prettier, and with some more info (like variables)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# Python Tools for Visual Studio debug server, running on port 5678")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PTVSD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# External js files are loaded from the internet as default")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("SELF_HOSTED")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("false")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("dev")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MODE")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"dev"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PTVSD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("BETTER_EXCEPTIONS_MAX_LENGTH")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("PLUGIN_PATHS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/data/opa/demo-plugins"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("-")]),t._v(" dynaconf_merge\n")])])])]),t._v(" "),a("h5",{attrs:{id:"simple-example"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#simple-example"}},[t._v("#")]),t._v(" Simple example")]),t._v(" "),a("div",{staticClass:"language-yaml extra-class"},[a("pre",{pre:!0,attrs:{class:"language-yaml"}},[a("code",[a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("myenv")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTIONAL_COMPONENTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("dynaconf_merge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean important"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("MYREDIS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("LOAD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auto"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("DRIVER")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis-walrus"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("OPTS")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token key atrule"}},[t._v("URL")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://some-external-redis"')]),t._v("\n")])])]),a("p",[t._v("In the example above:")]),t._v(" "),a("ul",[a("li",[t._v("It will use the "),a("code",[t._v("redis-walrus")]),t._v(" driver")]),t._v(" "),a("li",[t._v("Give you an instance of it named "),a("code",[t._v("myredis")]),t._v(" available in your plugins")]),t._v(" "),a("li",[t._v("Load if the hostname "),a("code",[t._v("some-external-redis")]),t._v(" answers")]),t._v(" "),a("li",[t._v("Merge with the other optional-components already defined ("),a("code",[t._v("dynaconf_merge")]),t._v(")")])]),t._v(" "),a("h4",{attrs:{id:"using"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#using"}},[t._v("#")]),t._v(" Using")]),t._v(" "),a("p",[t._v("To use an optional-component (lets say the one defined above), load it as a dependency in your route, like")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/counter"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("counter_sync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n myredis "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'myredis'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n counter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" myredis"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("incr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'counter'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string-interpolation"}},[a("span",{pre:!0,attrs:{class:"token string"}},[t._v("f'Counter is ")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("counter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'")])]),t._v("\n")])])]),a("p",[t._v("Note that "),a("code",[t._v("myredis")]),t._v(" is the component itself, not just the instance. There might be other functions available, like utility-functions.")]),t._v(" "),a("p",[t._v("Take a look at the "),a("a",{attrs:{href:"./optional-components-reference"}},[t._v("optional-components reference")]),t._v(" for a list of all builtin drivers/optional-components")]),t._v(" "),a("h4",{attrs:{id:"adding-a-driver"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#adding-a-driver"}},[t._v("#")]),t._v(" Adding a driver")]),t._v(" "),a("p",[t._v("You can add a driver using a plugin. For probably the best example, see the "),a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/blob/master/api/data/opa/plugins/driver_redis.py",target:"_blank",rel:"noopener noreferrer"}},[t._v("driver-redis"),a("OutboundLink")],1),t._v(" plugin at github (or below).")]),t._v(" "),a("details",{staticClass:"custom-block details"},[a("summary",[t._v("driver_redis.py")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" logging\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" aioredis "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" aioredislib\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" walrus "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("as")]),t._v(" walruslib\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Driver\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("utils "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" host_exists\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Aioredis")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'redis-aioredis'")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("connect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" host_exists"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("opts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("URL"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'database-url'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v("\n\n self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("await")]),t._v(" aioredislib"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("create_redis_pool"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("opts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("URL"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("async")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("disconnect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("close"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("wait_closed"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Walrus")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'redis-walrus'")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("connect")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("not")]),t._v(" host_exists"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("opts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("URL"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'database-url'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("False")]),t._v("\n\n self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" walruslib"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Database"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("from_url"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("opts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("URL"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("validate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n self"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("instance"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("client_id"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])]),t._v(" "),a("p",[t._v("In driver_redis you will see that a driver is just a class inherited from "),a("code",[t._v("opa.Driver")]),t._v(":")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("pm")]),t._v(" is the plugin-manager instance, that is available as "),a("code",[t._v("self.pm")]),t._v(" inside the driver-instance")]),t._v(" "),a("li",[a("code",[t._v("opts")]),t._v(" will be populated with the original OPTS for this driver")]),t._v(" "),a("li",[a("code",[t._v("connect()")]),t._v(" "),a("ul",[a("li",[t._v("Can by "),a("code",[t._v("async")]),t._v(" or "),a("code",[t._v("sync")]),t._v(".")]),t._v(" "),a("li",[t._v("Is run when we load the application.")]),t._v(" "),a("li",[a("code",[t._v("opts")]),t._v(" is the opts configured, example "),a("code",[t._v("opts.URL")])]),t._v(" "),a("li",[t._v("If the configuration "),a("code",[t._v("LOAD")]),t._v(" is set to "),a("code",[t._v("auto")]),t._v(" "),a("ul",[a("li",[t._v("This function needs to return False to signal that the pre-check (ie, hostname check) failed and it should not connect.. If it does not return False, we will validate try to validate the connection even if "),a("code",[t._v("LOAD=auto")])])])]),t._v(" "),a("li",[t._v("Is responsible for setting "),a("code",[t._v("self.instance")]),t._v(".")])])]),t._v(" "),a("li",[a("code",[t._v("validate()")]),t._v(":\n"),a("ul",[a("li",[t._v("Needs to be async if connect is async")]),t._v(" "),a("li",[t._v("Will run only if\n"),a("ul",[a("li",[a("code",[t._v("LOAD")]),t._v(" config is "),a("code",[t._v("yes")])]),t._v(" "),a("li",[t._v("or if connect "),a("code",[t._v("did not")]),t._v(" return "),a("code",[t._v("False")])])])]),t._v(" "),a("li",[t._v("Should raise an exception of any kind if it is not able to validate the connection.")])])]),t._v(" "),a("li",[a("code",[t._v("disconnect(self)")]),t._v(": Not implemented yet")]),t._v(" "),a("li",[a("code",[t._v("get_instance(self)")]),t._v(": Normally it just returns "),a("code",[t._v("self.instance")]),t._v(", but if you want, you can override it")])]),t._v(" "),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("If you want some logic in your driver, you can use "),a("code",[t._v("hooks")]),t._v(", just use "),a("code",[t._v("scall_hook")]),t._v(" or "),a("code",[t._v("call_hook_async")]),t._v(" as described "),a("a",{attrs:{href:"#hook"}},[t._v("above")]),t._v(".")])]),t._v(" "),a("h3",{attrs:{id:"api-s-and-routes"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#api-s-and-routes"}},[t._v("#")]),t._v(" API's and routes")]),t._v(" "),a("p",[t._v("If you want to add a route accessible via an api, there are multiple ways to do it.")]),t._v(" "),a("p",[t._v("Expose an "),a("code",[t._v("APIRouter")]),t._v(" instance named "),a("code",[t._v("router")]),t._v(" in your plugin is probably the easiest. It will work for most of the usecases.")]),t._v(" "),a("div",{staticClass:"language-py extra-class"},[a("pre",{pre:!0,attrs:{class:"language-py"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" opa "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" get_router\nrouter "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" get_router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token decorator annotation punctuation"}},[t._v("@router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("root")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Hello'")]),t._v("\n")])])]),a("div",{staticClass:"custom-block tip"},[a("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),a("p",[t._v("The "),a("code",[t._v("get_router")]),t._v(" function is nothing magical, it is basicly the same as "),a("code",[t._v("fastapi.APIRouter()")]),t._v(".")])]),t._v(" "),a("p",[t._v("Another way is to use the "),a("code",[t._v("Setup")]),t._v(" plugin. It will get the main fastapi "),a("code",[t._v("app")]),t._v(" object as input, which you can use to add a route or do more powerfull things.")]),t._v(" "),a("h2",{attrs:{id:"metadata"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#metadata"}},[t._v("#")]),t._v(" Metadata")]),t._v(" "),a("p",[t._v("A plugin can have metadata attached to it in form of a "),a("code",[t._v("json")]),t._v(" file.")]),t._v(" "),a("ul",[a("li",[t._v("If it's a flat "),a("code",[t._v(".py")]),t._v(" file. Create a file with "),a("code",[t._v("-meta.json")]),t._v(" "),a("ul",[a("li",[t._v("Exammple on "),a("code",[t._v("my_utils.py")]),t._v(" you will have "),a("code",[t._v("my_utils-meta.json")])])])]),t._v(" "),a("li",[t._v("If it is a python package, make "),a("code",[t._v("/meta.json")]),t._v(" in the same folder as the package.")])]),t._v(" "),a("p",[t._v("Currently, you can only define tags as metadata, which can be used for filtering ("),a("code",[t._v("PLUGIN_WHITELIST_TAGS")]),t._v(" and "),a("code",[t._v("PLUGIN_BLACKLIST_TAGS")]),t._v(") which plugins to load.")]),t._v(" "),a("p",[t._v("Example")]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"tags"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"utils"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"admin"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"examples"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#examples"}},[t._v("#")]),t._v(" Examples")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/api/data/opa/demo-plugins",target:"_blank",rel:"noopener noreferrer"}},[t._v("demo-plugins"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/api/data/opa/plugins",target:"_blank",rel:"noopener noreferrer"}},[t._v("core-plugins"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/opa-stack/opa-stack/tree/master/examples",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),a("OutboundLink")],1)])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/15.79dc74f3.js b/assets/js/15.79dc74f3.js new file mode 100644 index 0000000..bf70bd2 --- /dev/null +++ b/assets/js/15.79dc74f3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{225:function(t,e,o){"use strict";o.r(e);var a=o(0),r=Object(a.a)({},(function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("h1",{attrs:{id:"more-info"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#more-info"}},[t._v("#")]),t._v(" More info")]),t._v(" "),o("h2",{attrs:{id:"project-goal"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#project-goal"}},[t._v("#")]),t._v(" Project goal")]),t._v(" "),o("p",[t._v("Working as an engineer/devop-person for some years, one thing often missing is a home for your code doing very custom things, specially logic you often want in the middle of other API's.")]),t._v(" "),o("p",[t._v("The requirements was therefor more or less:")]),t._v(" "),o("ul",[o("li",[t._v("Need for python3, non-blocking, async and modern")]),t._v(" "),o("li",[t._v("A place to store non-relational temporary (or semi-permanent) data")]),t._v(" "),o("li",[t._v("Job queue for things that might take time or you want custom schedule")]),t._v(" "),o("li",[t._v("Good self-documenting api endpoints for building other tools on. rest, graphql and websocket out of the box")]),t._v(" "),o("li",[t._v("Be dynamic enough to support multiple instances of it running, with some optional communication between them. (Example for different security contexts)")]),t._v(" "),o("li",[t._v("Good support for an out-of-the-box k8s deployment, including scalability")]),t._v(" "),o("li",[t._v("A way to do almost everything via plugins")]),t._v(" "),o("li",[t._v("An optional component system where you can just attach some additional services, like redis, and have it working")])]),t._v(" "),o("h2",{attrs:{id:"faq"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#faq"}},[t._v("#")]),t._v(" FAQ")]),t._v(" "),o("h3",{attrs:{id:"why-not-use-something-out-there-example-node-red"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#why-not-use-something-out-there-example-node-red"}},[t._v("#")]),t._v(" Why not use something out there? Example node-red..")]),t._v(" "),o("p",[t._v("There are a lot of nice tooling out there that solves similar problems. But I really coulnt find anything that was dynamic enough to support what I wanted/needed.")]),t._v(" "),o("h3",{attrs:{id:"but-what-about-just-setting-up-everything-yourself"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#but-what-about-just-setting-up-everything-yourself"}},[t._v("#")]),t._v(" But what about just setting up everything yourself..?")]),t._v(" "),o("p",[t._v("That is just what this project is trying to do, but doing it in a way so you can use plugins with your actual code, without needing to setup a whole stack. opa-stack tries to gives you a modern base, with minimal efford to start coding.")]),t._v(" "),o("p",[t._v("Take a look at "),o("a",{attrs:{href:"#what-it-s-made-of"}},[t._v("what it's made of")]),t._v(" below for information what this stack is made of")]),t._v(" "),o("h3",{attrs:{id:"but-then-what-about-other-project-templates-cookiecutter-and-other-examples"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#but-then-what-about-other-project-templates-cookiecutter-and-other-examples"}},[t._v("#")]),t._v(" But... Then what about other project templates, cookiecutter and other examples")]),t._v(" "),o("p",[t._v("Fair point.. There are a lot of good start-templates out there, both for "),o("a",{attrs:{href:"https://fastapi.tiangolo.com/project-generation/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FastAPI"),o("OutboundLink")],1),t._v(" and "),o("a",{attrs:{href:"https://github.com/cookiecutter/cookiecutter",target:"_blank",rel:"noopener noreferrer"}},[t._v("cookiecutter"),o("OutboundLink")],1),t._v(".\nAnd in many cases, they might be better. Example if you are making a big website, or a custom api for a chat platform (or ...).")]),t._v(" "),o("p",[t._v("What the opa-stack gives you is a powerfull platform for where you can do very custom things, but as plugins. Those plugins have access to things they need to be a good api, and to talk to other api's.")]),t._v(" "),o("h3",{attrs:{id:"when-should-i-not-use-opa-stack"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#when-should-i-not-use-opa-stack"}},[t._v("#")]),t._v(" When should I not use opa-stack?")]),t._v(" "),o("p",[t._v("Hard to say, but I woulnt want to use it if the project will have dedicated people working on it, then it might be a good thing starting from scratch, or maybe from a cookiecutter template.")]),t._v(" "),o("p",[t._v("Feel free to use it as an api-endpoint for other parts of your project tho. Let say your main work is on the frontend, but you need an api for something custom..")]),t._v(" "),o("h3",{attrs:{id:"when-should-i-use-the-opa-stack"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#when-should-i-use-the-opa-stack"}},[t._v("#")]),t._v(" When should I use the opa-stack?")]),t._v(" "),o("p",[t._v("Read some docs, see if it fits. If you are a team that needs to talk to a bunch of api's and do custom things using python, it is probably a good fit.")]),t._v(" "),o("p",[t._v("Or maybe you have a team of 20 people, all going to eventually wanting to do a little thing in python where they want access to it via an api, or any other channel.")]),t._v(" "),o("p",[t._v("Opa-stack might also be a good fit if you want a python engine that does something, if something.. You don't haveto use the api-part 😃")]),t._v(" "),o("h3",{attrs:{id:"whats-about-the-name"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#whats-about-the-name"}},[t._v("#")]),t._v(" Whats about the name?")]),t._v(" "),o("p",[t._v("The "),o("code",[t._v("OP")]),t._v(" in "),o("code",[t._v("OPA")]),t._v(" stands for "),o("code",[t._v("ops")]),t._v(", as in a group of people/mindset (yes, yes.. "),o("code",[t._v("dev")]),t._v(" is not a part of the name, but it's kinda implicit given the whole name.. kinda 😃).")]),t._v(" "),o("p",[t._v("The "),o("code",[t._v("A")]),t._v(" is for API or Automation, or both. You choose 😏")]),t._v(" "),o("h2",{attrs:{id:"what-it-s-made-of"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#what-it-s-made-of"}},[t._v("#")]),t._v(" What it's made of")]),t._v(" "),o("ul",[o("li",[o("p",[t._v("API (main component)")]),t._v(" "),o("ul",[o("li",[t._v("Centos8 "),o("a",{attrs:{href:"https://hub.docker.com/r/opastack/base-centos",target:"_blank",rel:"noopener noreferrer"}},[t._v("base-image"),o("OutboundLink")],1),t._v(" with "),o("a",{attrs:{href:"https://github.com/just-containers/s6-overlay",target:"_blank",rel:"noopener noreferrer"}},[t._v("s6-overlay"),o("OutboundLink")],1),t._v(" as init")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://www.mongodb.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mongodb"),o("OutboundLink")],1),t._v(" as general database")]),t._v(" "),o("li",[t._v("Python 3.6+\n"),o("ul",[o("li",[t._v("Using typing")]),t._v(" "),o("li",[t._v("Usinc async where possible")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://fastapi.tiangolo.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FastAPI"),o("OutboundLink")],1),t._v(" for web\n"),o("ul",[o("li",[o("a",{attrs:{href:"https://www.uvicorn.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("uvicorn"),o("OutboundLink")],1),t._v(" as asgi server")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://pydantic-docs.helpmanual.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Pydantic"),o("OutboundLink")],1),t._v(" for api modelling")])])]),t._v(" "),o("li",[o("a",{attrs:{href:"https://motor.readthedocs.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("motor"),o("OutboundLink")],1),t._v(" for comminicating with mongo")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://dynaconf.readthedocs.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("dynaconf"),o("OutboundLink")],1),t._v(" for configuration management")])])])])]),t._v(" "),o("li",[o("p",[t._v("Docs")]),t._v(" "),o("ul",[o("li",[o("RouterLink",{attrs:{to:"/more-info/vuepress.vuejs.org/"}},[t._v("vuepress")]),t._v(" for generating "),o("a",{attrs:{href:"https://github.com/opa-stack/opa-stack.github.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("docs"),o("OutboundLink")],1)],1),t._v(" "),o("li",[o("a",{attrs:{href:"https://yarnpkg.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("yarn"),o("OutboundLink")],1),t._v(" to keep track of packages/plugins")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://analytics.google.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Google analytics"),o("OutboundLink")],1),t._v(" to see some site-stats")]),t._v(" "),o("li",[o("a",{attrs:{href:"https://www.algolia.com/ref/docsearch/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Algolia"),o("OutboundLink")],1),t._v(" for searching")])])]),t._v(" "),o("li",[o("p",[t._v("Development")]),t._v(" "),o("ul",[o("li",[t._v("Everything under "),o("a",{attrs:{href:"https://github.com/opa-stack",target:"_blank",rel:"noopener noreferrer"}},[t._v("github"),o("OutboundLink")],1)]),t._v(" "),o("li",[o("a",{attrs:{href:"https://travis-ci.org/opa-stack/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Travis-ci"),o("OutboundLink")],1),t._v(" for stuff")]),t._v(" "),o("li",[t._v("An "),o("a",{attrs:{href:"https://hub.docker.com/orgs/opastack",target:"_blank",rel:"noopener noreferrer"}},[t._v("organization"),o("OutboundLink")],1),t._v(" on hub.docker.io for container-images")])])])])])}),[],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/16.bff4d9f4.js b/assets/js/16.bff4d9f4.js new file mode 100644 index 0000000..75519e4 --- /dev/null +++ b/assets/js/16.bff4d9f4.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{226:function(t,s,a){"use strict";a.r(s);var e=a(0),o=Object(e.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"contact"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#contact"}},[t._v("#")]),t._v(" Contact")]),t._v(" "),a("ul",[a("li",[t._v("Issues: https://github.com/opa-stack/opa-stack/issues")]),t._v(" "),a("li",[t._v("Pull-requests: https://github.com/opa-stack/opa-stack/pulls")]),t._v(" "),a("li",[t._v("Security-issues: @xeor at keybase")]),t._v(" "),a("li",[t._v("Other: opa@boa.nu")])])])}),[],!1,null,null,null);s.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/17.7b74f075.js b/assets/js/17.7b74f075.js new file mode 100644 index 0000000..eb4af41 --- /dev/null +++ b/assets/js/17.7b74f075.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{227:function(t,a,o){"use strict";o.r(a);var s=o(0),e=Object(s.a)({},(function(){var t=this,a=t.$createElement,o=t._self._c||a;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("p",{attrs:{align:"center"}},[o("em",[t._v("Ops (API|Automation) stack")])]),t._v(" "),o("hr"),t._v(" "),o("p",[o("strong",[t._v("Documentation")]),t._v(": "),o("a",{attrs:{href:"https://opa-stack.github.io",target:"_blank"}},[t._v("https://opa-stack.github.io")])]),t._v(" "),o("p",[o("strong",[t._v("Source Code")]),t._v(": "),o("a",{attrs:{href:"https://github.com/opa-stack/opa-stack",target:"_blank"}},[t._v("https://github.com/opa-stack/opa-stack")])]),t._v(" "),o("hr")])}),[],!1,null,null,null);a.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/18.718f9d0a.js b/assets/js/18.718f9d0a.js new file mode 100644 index 0000000..bc0792e --- /dev/null +++ b/assets/js/18.718f9d0a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{55:function(n,w,o){}}]); \ No newline at end of file diff --git a/assets/js/3.ab9a3b8e.js b/assets/js/3.ab9a3b8e.js new file mode 100644 index 0000000..7b6f3a9 --- /dev/null +++ b/assets/js/3.ab9a3b8e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[3],Array(56).concat([function(t,e,n){var r=n(24)("wks"),i=n(25),o=n(1).Symbol,a="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=a&&o[t]||(a?o:i)("Symbol."+t))}).store=r},function(t,e,n){var r=n(117)("wks"),i=n(118),o=n(60).Symbol,a="function"==typeof o;(t.exports=function(t){return r[t]||(r[t]=a&&o[t]||(a?o:i)("Symbol."+t))}).store=r},function(t,e,n){"use strict";n.d(e,"d",(function(){return r})),n.d(e,"a",(function(){return o})),n.d(e,"i",(function(){return a})),n.d(e,"f",(function(){return u})),n.d(e,"g",(function(){return c})),n.d(e,"h",(function(){return l})),n.d(e,"b",(function(){return f})),n.d(e,"e",(function(){return p})),n.d(e,"k",(function(){return h})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return v})),n.d(e,"j",(function(){return g}));const r=/#.*$/,i=/\.(md|html)$/,o=/\/$/,a=/^[a-z]+:/i;function s(t){return decodeURI(t).replace(r,"").replace(i,"")}function u(t){return a.test(t)}function c(t){return/^mailto:/.test(t)}function l(t){return/^tel:/.test(t)}function f(t){if(u(t))return t;const e=t.match(r),n=e?e[0]:"",i=s(t);return o.test(i)?t:i+".html"+n}function p(t,e){const n=t.hash,i=function(t){const e=t.match(r);if(e)return e[0]}(e);return(!i||n===i)&&s(t.path)===s(e)}function h(t,e,n){if(u(e))return{type:"external",path:e};n&&(e=function(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const i=e.split("/");n&&i[i.length-1]||i.pop();const o=t.replace(/^\//,"").split("/");for(let t=0;t({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}(t);const s=a.sidebar||o.sidebar;if(s){const{base:t,config:n}=function(t,e){if(Array.isArray(e))return{base:"/",config:e};for(const r in e)if(0===(n=t,/(\.html|\/)$/.test(n)?n:n+"/").indexOf(encodeURI(r)))return{base:r,config:e[r]};var n;return{}}(e,s);return n?n.map(e=>function t(e,n,r,i=1){if("string"==typeof e)return h(n,e,r);if(Array.isArray(e))return Object.assign(h(n,e[0],r),{title:e[1]});{i>3&&console.error("[vuepress] detected a too deep nested sidebar group.");const o=e.children||[];return 0===o.length&&e.path?Object.assign(h(n,e.path,r),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,children:o.map(e=>t(e,n,r,i+1)),collapsable:!1!==e.collapsable}}}(e,i,t)):[]}return[]}function v(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function g(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},function(t,e){var n=t.exports={version:"2.6.11"};"number"==typeof __e&&(__e=n)},function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,e){t.exports={}},function(t,e,n){var r=n(68),i=n(78);t.exports=n(69)?function(t,e,n){return r.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){var r=n(77);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){var r=n(16);t.exports=function(t){return Object(r(t))}},function(t,e,n){"use strict";var r=n(30),i=n(66)(3);r(r.P+r.F*!n(67)([].some,!0),"Array",{some:function(t){return i(this,t,arguments[1])}})},function(t,e,n){var r=n(21),i=n(35),o=n(64),a=n(31),s=n(137);t.exports=function(t,e){var n=1==t,u=2==t,c=3==t,l=4==t,f=6==t,p=5==t||f,h=e||s;return function(e,s,d){for(var v,g,m=o(e),b=i(m),y=r(s,d,3),x=a(b.length),_=0,k=n?h(e,x):u?h(e,0):void 0;x>_;_++)if((p||_ in b)&&(g=y(v=b[_],_,m),t))if(n)k[_]=g;else if(g)switch(t){case 3:return!0;case 5:return v;case 6:return _;case 2:k.push(v)}else if(l)return!1;return f?-1:c||l?l:k}}},function(t,e,n){"use strict";var r=n(4);t.exports=function(t,e){return!!t&&r((function(){e?t.call(null,(function(){}),1):t.call(null)}))}},function(t,e,n){var r=n(63),i=n(147),o=n(148),a=Object.defineProperty;e.f=n(69)?Object.defineProperty:function(t,e,n){if(r(t),e=o(e,!0),r(n),i)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){t.exports=!n(111)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e,n){for(var r=n(132),i=n(33),o=n(18),a=n(1),s=n(17),u=n(73),c=n(56),l=c("iterator"),f=c("toStringTag"),p=u.Array,h={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},d=i(h),v=0;v]*>)/g,h=/\$([$&`']|\d\d?)/g;n(106)("replace",2,(function(t,e,n,d){return[function(r,i){var o=t(this),a=null==r?void 0:r[e];return void 0!==a?a.call(r,o,i):n.call(String(o),r,i)},function(t,e){var i=d(n,t,this,e);if(i.done)return i.value;var f=r(t),p=String(this),h="function"==typeof e;h||(e=String(e));var g=f.global;if(g){var m=f.unicode;f.lastIndex=0}for(var b=[];;){var y=u(f,p);if(null===y)break;if(b.push(y),!g)break;""===String(y[0])&&(f.lastIndex=s(p,o(f.lastIndex),m))}for(var x,_="",k=0,L=0;L=k&&(_+=p.slice(k,O)+A,k=O+S.length)}return _+p.slice(k)}];function v(t,e,r,o,a,s){var u=r+t.length,c=o.length,l=h;return void 0!==a&&(a=i(a),l=p),n.call(s,l,(function(n,i){var s;switch(i.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,r);case"'":return e.slice(u);case"<":s=a[i.slice(1,-1)];break;default:var l=+i;if(0===l)return n;if(l>c){var p=f(l/10);return 0===p?n:p<=c?void 0===o[p-1]?i.charAt(1):o[p-1]+i.charAt(1):n}s=o[l-1]}return void 0===s?"":s}))}}))},function(t,e,n){var r=n(60),i=n(59),o=n(110),a=n(62),s=n(70),u=function(t,e,n){var c,l,f,p=t&u.F,h=t&u.G,d=t&u.S,v=t&u.P,g=t&u.B,m=t&u.W,b=h?i:i[e]||(i[e]={}),y=b.prototype,x=h?r:d?r[e]:(r[e]||{}).prototype;for(c in h&&(n=e),n)(l=!p&&x&&void 0!==x[c])&&s(b,c)||(f=l?x[c]:n[c],b[c]=h&&"function"!=typeof x[c]?n[c]:g&&l?o(f,r):m&&x[c]==f?function(t){var e=function(e,n,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,n)}return new t(e,n,r)}return t.apply(this,arguments)};return e.prototype=t.prototype,e}(f):v&&"function"==typeof f?o(Function.call,f):f,v&&((b.virtual||(b.virtual={}))[c]=f,t&u.R&&y&&!y[c]&&a(y,c,f)))};u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(155),i=n(81);t.exports=function(t){return r(i(t))}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(117)("keys"),i=n(118);t.exports=function(t){return r[t]||(r[t]=i(t))}},function(t,e,n){"use strict";var r=n(166)(!0);n(114)(String,"String",(function(t){this._t=String(t),this._i=0}),(function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})}))},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},,function(t,e,n){"use strict";var r=n(37),i=n(30),o=n(18),a=n(17),s=n(73),u=n(134),c=n(100),l=n(135),f=n(56)("iterator"),p=!([].keys&&"next"in[].keys()),h=function(){return this};t.exports=function(t,e,n,d,v,g,m){u(n,e,d);var b,y,x,_=function(t){if(!p&&t in O)return O[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},k=e+" Iterator",L="values"==v,S=!1,O=t.prototype,C=O[f]||O["@@iterator"]||v&&O[v],w=C||_(v),$=v?L?_("entries"):w:void 0,j="Array"==e&&O.entries||C;if(j&&(x=l(j.call(new t)))!==Object.prototype&&x.next&&(c(x,k,!0),r||"function"==typeof x[f]||a(x,f,h)),L&&C&&"values"!==C.name&&(S=!0,w=function(){return C.call(this)}),r&&!m||!p&&!S&&O[f]||a(O,f,w),s[e]=w,s[k]=h,v)if(b={values:L?w:_("values"),keys:g?w:_("keys"),entries:$},m)for(y in b)y in O||o(O,y,b[y]);else i(i.P+i.F*(p||S),e,b);return b}},function(t,e,n){var r=n(11).f,i=n(6),o=n(56)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(64),i=n(33);n(136)("keys",(function(){return function(t){return i(r(t))}}))},function(t,e,n){var r=n(19);t.exports=Array.isArray||function(t){return"Array"==r(t)}},function(t,e,n){"use strict";var r=n(104)(!0);t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},function(t,e,n){var r=n(20),i=n(16);t.exports=function(t){return function(e,n){var o,a,s=String(i(e)),u=r(n),c=s.length;return u<0||u>=c?t?"":void 0:(o=s.charCodeAt(u))<55296||o>56319||u+1===c||(a=s.charCodeAt(u+1))<56320||a>57343?t?s.charAt(u):o:t?s.slice(u,u+2):a-56320+(o-55296<<10)+65536}}},function(t,e,n){"use strict";var r=n(142),i=RegExp.prototype.exec;t.exports=function(t,e){var n=t.exec;if("function"==typeof n){var o=n.call(t,e);if("object"!=typeof o)throw new TypeError("RegExp exec method returned something other than an Object or null");return o}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return i.call(t,e)}},function(t,e,n){"use strict";n(143);var r=n(18),i=n(17),o=n(4),a=n(16),s=n(56),u=n(107),c=s("species"),l=!o((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),f=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var p=s(t),h=!o((function(){var e={};return e[p]=function(){return 7},7!=""[t](e)})),d=h?!o((function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[c]=function(){return n}),n[p](""),!e})):void 0;if(!h||!d||"replace"===t&&!l||"split"===t&&!f){var v=/./[p],g=n(a,p,""[t],(function(t,e,n,r,i){return e.exec===u?h&&!i?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}})),m=g[0],b=g[1];r(String.prototype,t,m),i(RegExp.prototype,p,2==e?function(t,e){return b.call(t,this,e)}:function(t){return b.call(t,this)})}}},function(t,e,n){"use strict";var r,i,o=n(108),a=RegExp.prototype.exec,s=String.prototype.replace,u=a,c=(r=/a/,i=/b*/g,a.call(r,"a"),a.call(i,"a"),0!==r.lastIndex||0!==i.lastIndex),l=void 0!==/()??/.exec("")[1];(c||l)&&(u=function(t){var e,n,r,i,u=this;return l&&(n=new RegExp("^"+u.source+"$(?!\\s)",o.call(u))),c&&(e=u.lastIndex),r=a.call(u,t),c&&r&&(u.lastIndex=u.global?r.index+r[0].length:e),l&&r&&r.length>1&&s.call(r[0],n,(function(){for(i=1;i0?i(r(t),9007199254740991):0}},function(t,e,n){var r=n(59),i=n(60),o=i["__core-js_shared__"]||(i["__core-js_shared__"]={});(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(115)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){var r=n(68).f,i=n(70),o=n(57)("toStringTag");t.exports=function(t,e,n){t&&!i(t=n?t:t.prototype,o)&&r(t,o,{configurable:!0,value:e})}},function(t,e,n){var r=n(81);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(123),i=n(57)("iterator"),o=n(61);t.exports=n(59).getIteratorMethod=function(t){if(null!=t)return t[i]||t["@@iterator"]||o[r(t)]}},function(t,e,n){var r=n(79),i=n(57)("toStringTag"),o="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),i))?n:o?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){t.exports=n(168)},function(t,e,n){var r=n(3),i=n(19),o=n(56)("match");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[o])?!!e:"RegExp"==i(t))}},function(t,e,n){"use strict";var r=n(5),i=n(31),o=n(103),a=n(105);n(106)("match",1,(function(t,e,n,s){return[function(n){var r=t(this),i=null==n?void 0:n[e];return void 0!==i?i.call(n,r):new RegExp(n)[e](String(r))},function(t){var e=s(n,t,this);if(e.done)return e.value;var u=r(t),c=String(this);if(!u.global)return a(u,c);var l=u.unicode;u.lastIndex=0;for(var f,p=[],h=0;null!==(f=a(u,c));){var d=String(f[0]);p[h]=d,""===d&&(u.lastIndex=o(c,i(u.lastIndex),l)),h++}return 0===h?null:p}]}))},function(t,e,n){"use strict";var r=n(30),i=n(66)(1);r(r.P+r.F*!n(67)([].map,!0),"Array",{map:function(t){return i(this,t,arguments[1])}})},function(t,e){t.exports=function(t){return null==t}},function(t,e,n){var r=n(199).Symbol;t.exports=r},function(t,e,n){"use strict";n.r(e);n(65);var r=n(58),i={name:"SidebarGroup",components:{DropdownTransition:n(131).a},props:["item","open","collapsable","depth"],beforeCreate:function(){this.$options.components.SidebarLinks=n(130).default},methods:{isActive:r.e}},o=(n(207),n(0)),a=Object(o.a)(i,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?n("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):n("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),n("DropdownTransition",[t.open||!t.collapsable?n("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null).exports;n(127),n(208);function s(t,e,n,r,i){var o={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:r,"sidebar-link":!0}};return i>2&&(o.style={"padding-left":i+"rem"}),t("RouterLink",o,n)}function u(t,e,n,i,o){var a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1;return!e||a>o?null:t("ul",{class:"sidebar-sub-headers"},e.map((function(e){var c=Object(r.e)(i,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[s(t,n+"#"+e.slug,e.title,c,e.level-1),u(t,e.children,n,i,o,a+1)])})))}var c={functional:!0,props:["item","sidebarDepth"],render:function(t,e){var n=e.parent,i=n.$page,o=(n.$site,n.$route),a=n.$themeConfig,c=n.$themeLocaleConfig,l=e.props,f=l.item,p=l.sidebarDepth,h=Object(r.e)(o,f.path),d="auto"===f.type?h||f.children.some((function(t){return Object(r.e)(o,f.basePath+"#"+t.slug)})):h,v="external"===f.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,f.path,f.title||f.path):s(t,f.path,f.title||f.path,d),g=[i.frontmatter.sidebarDepth,p,c.sidebarDepth,a.sidebarDepth,1].find((function(t){return void 0!==t})),m=c.displayAllHeaders||a.displayAllHeaders;return"auto"===f.type?[v,u(t,f.children,f.basePath,o,g)]:(d||m)&&f.headers&&!r.d.test(f.path)?[v,u(t,Object(r.c)(f.headers),f.path,o,g)]:v}};n(209);function l(t,e){return"group"===e.type&&e.children.some((function(e){return"group"===e.type?l(t,e):"page"===e.type&&Object(r.e)(t,e.path)}))}var f={name:"SidebarLinks",components:{SidebarGroup:a,SidebarLink:Object(o.a)(c,void 0,void 0,!1,null,null,null).exports},props:["items","depth","sidebarDepth"],data:function(){return{openGroupIndex:0}},watch:{$route:function(){this.refreshIndex()}},created:function(){this.refreshIndex()},methods:{refreshIndex:function(){var t=function(t,e){for(var n=0;n-1&&(this.openGroupIndex=t)},toggleGroup:function(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive:function(t){return Object(r.e)(this.$route,t.regularPath)}}},p=Object(o.a)(f,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.items.length?n("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(e,r){return n("li",{key:r},["group"===e.type?n("SidebarGroup",{attrs:{item:e,open:r===t.openGroupIndex,collapsable:e.collapsable||e.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(r)}}}):n("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:e}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=p.exports},function(t,e,n){"use strict";var r={name:"DropdownTransition",methods:{setHeight:function(t){t.style.height=t.scrollHeight+"px"},unsetHeight:function(t){t.style.height=""}}},i=(n(191),n(0)),o=Object(i.a)(r,(function(){var t=this.$createElement;return(this._self._c||t)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.a=o.exports},function(t,e,n){"use strict";var r=n(72),i=n(133),o=n(73),a=n(12);t.exports=n(99)(Array,"Array",(function(t,e){this._t=a(t),this._i=0,this._k=e}),(function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e,n){"use strict";var r=n(39),i=n(22),o=n(100),a={};n(17)(a,n(56)("iterator"),(function(){return this})),t.exports=function(t,e,n){t.prototype=r(a,{next:i(1,n)}),o(t,e+" Iterator")}},function(t,e,n){var r=n(6),i=n(64),o=n(23)("IE_PROTO"),a=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=i(t),r(t,o)?t[o]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?a:null}},function(t,e,n){var r=n(30),i=n(13),o=n(4);t.exports=function(t,e){var n=(i.Object||{})[t]||Object[t],a={};a[t]=e(n),r(r.S+r.F*o((function(){n(1)})),"Object",a)}},function(t,e,n){var r=n(138);t.exports=function(t,e){return new(r(t))(e)}},function(t,e,n){var r=n(3),i=n(102),o=n(56)("species");t.exports=function(t){var e;return i(t)&&("function"!=typeof(e=t.constructor)||e!==Array&&!i(e.prototype)||(e=void 0),r(e)&&null===(e=e[o])&&(e=void 0)),void 0===e?Array:e}},function(t,e,n){"use strict";n(140)("link",(function(t){return function(e){return t(this,"a","href",e)}}))},function(t,e,n){var r=n(30),i=n(4),o=n(16),a=/"/g,s=function(t,e,n,r){var i=String(o(t)),s="<"+e;return""!==n&&(s+=" "+n+'="'+String(r).replace(a,""")+'"'),s+">"+i+""};t.exports=function(t,e){var n={};n[t]=e(s),r(r.P+r.F*i((function(){var e=""[t]('"');return e!==e.toLowerCase()||e.split('"').length>3})),"String",n)}},function(t,e,n){"use strict";var r=n(74);n.n(r).a},function(t,e,n){var r=n(19),i=n(56)("toStringTag"),o="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),i))?n:o?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){"use strict";var r=n(107);n(30)({target:"RegExp",proto:!0,forced:r!==/./.exec},{exec:r})},function(t,e,n){n(145),t.exports=n(59).Array.isArray},function(t,e,n){var r=n(76);r(r.S,"Array",{isArray:n(149)})},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e,n){t.exports=!n(69)&&!n(111)((function(){return 7!=Object.defineProperty(n(112)("div"),"a",{get:function(){return 7}}).a}))},function(t,e,n){var r=n(77);t.exports=function(t,e){if(!r(t))return t;var n,i;if(e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;if("function"==typeof(n=t.valueOf)&&!r(i=n.call(t)))return i;if(!e&&"function"==typeof(n=t.toString)&&!r(i=n.call(t)))return i;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(79);t.exports=Array.isArray||function(t){return"Array"==r(t)}},function(t,e,n){t.exports=n(151)},function(t,e,n){n(113),n(84),t.exports=n(167)},function(t,e,n){"use strict";var r=n(153),i=n(154),o=n(61),a=n(80);t.exports=n(114)(Array,"Array",(function(t,e){this._t=a(t),this._i=0,this._k=e}),(function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,i(1)):i(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),o.Arguments=o.Array,r("keys"),r("values"),r("entries")},function(t,e){t.exports=function(){}},function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e,n){var r=n(79);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e,n){t.exports=n(62)},function(t,e,n){"use strict";var r=n(158),i=n(78),o=n(120),a={};n(62)(a,n(57)("iterator"),(function(){return this})),t.exports=function(t,e,n){t.prototype=r(a,{next:i(1,n)}),o(t,e+" Iterator")}},function(t,e,n){var r=n(63),i=n(159),o=n(119),a=n(83)("IE_PROTO"),s=function(){},u=function(){var t,e=n(112)("iframe"),r=o.length;for(e.style.display="none",n(164).appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write(" + + diff --git a/guide/api/plugin-system.html b/guide/api/plugin-system.html new file mode 100644 index 0000000..fdf5fc8 --- /dev/null +++ b/guide/api/plugin-system.html @@ -0,0 +1,530 @@ + + + + + + Plugin system | opa-stack + + + + + + + + +

# Plugin system

The plugin-system are powerfull and should cover most use-cases.

TIP

If you want to peak at code that drives the plugin-system, it's on github or just expand it here

plugin.py
import os
+import re
+import sys
+import json
+import inspect
+import asyncio
+import logging
+import pkgutil
+from collections import defaultdict
+from importlib import import_module
+from typing import Dict, Any, List, Callable
+
+from fastapi import FastAPI, APIRouter
+
+from opa.utils import unique, filter_dict_to_function
+from opa import config
+
+
+class BasePlugin:
+    pass
+
+
+class Driver(BasePlugin):
+    name: str
+    instance: Any = None
+    opts: Dict[str, Any]
+    pm: 'PluginManager'
+
+    def __init__(self, opts=None, load='auto'):
+        self.opts = opts or {}
+        self.load = load
+
+    def _pre_connection_check(self, connectionstatus, load):
+        if connectionstatus == False:  # if no host found
+            if self.load == 'yes':
+                raise Exception(
+                    f'Connect pre-check failed for {self.name}, as if the host is not there? Options {self.opts}'
+                )
+            else:
+                return False
+
+        if hasattr(self, 'validate'):
+            return True
+
+    def initialize(self):
+        connectionstatus = self.connect()
+        if self._pre_connection_check(connectionstatus, self.load):
+            self.validate()
+
+    async def initialize_async(self):
+        connectionstatus = await self.connect()
+        if self._pre_connection_check(connectionstatus, self.load):
+            await self.validate()
+
+    def get_instance(self):
+        return self.instance
+
+
+class HookDefinition(BasePlugin):
+    required: bool = False
+    is_async: bool = False
+    _used: bool = False
+
+
+class Hook(BasePlugin):
+    name: str
+    order: int = 0
+
+
+class Setup(BasePlugin):
+    ...
+
+
+def get_defined_plugins(mod, plugin_types=None):
+    plugin_types = plugin_types or ['hook-definitions', 'hooks', 'drivers', 'setup']
+    returndata = defaultdict(list)
+
+    for name, obj in inspect.getmembers(mod, inspect.isclass):
+        if mod.__name__ != obj.__module__:
+            # We don't want to load plugins/modules if they are imported, ie 'from ... import AnotherPlugin'
+            continue
+
+        if obj is Hook or obj is Driver or obj is Setup:
+            continue
+
+        if issubclass(obj, Hook) and 'hooks' in plugin_types:
+            returndata['hooks'].append(obj)
+        elif issubclass(obj, HookDefinition) and 'hook-definitions' in plugin_types:
+            returndata['hook-definitions'].append(obj)
+        elif issubclass(obj, Driver) and 'drivers' in plugin_types:
+            returndata['drivers'].append(obj)
+        elif issubclass(obj, Setup) and 'setup' in plugin_types:
+            returndata['setup'].append(obj)
+    return returndata
+
+
+class PluginManager:
+    status: Dict[str, Dict]
+
+    drivers: Dict[str, Driver]
+    optional_components: Dict[str, Driver]
+    hooks: Dict[str, List[Hook]]
+    hook_definitions: Dict[str, Hook]
+
+    store: Dict[str, Any]
+
+    def __init__(self):
+        self.status = defaultdict(dict)
+        self.drivers = {}
+        self.optional_components = {}
+        self.hooks = defaultdict(list)
+        self.hook_definitions = {}
+
+        self.store = {'task_candidates': []}
+
+    def post_hook(self):
+        final_hooks: Dict[str, Hook] = {}
+        for hook_name, hooks in self.hooks.items():
+            definition = self.hook_definitions.get(hook_name)
+            try:
+                hook_to_use = sorted(hooks, key=lambda x: x.order)[-1]
+            except IndexError:
+                continue
+
+            if definition:
+                definition._used = True
+            final_hooks[hook_name] = hook_to_use
+
+        should_be_used = [
+            name
+            for name, definition in self.hook_definitions.items()
+            if all([definition.required, not definition._used])
+        ]
+        if should_be_used:
+            raise Exception(f'Hooks that should be registered: {should_be_used}')
+
+        self.hooks = final_hooks
+
+    def register_driver(self, driver: Driver):
+        name = driver.name.lower()
+        if name in self.drivers:
+            if self.drivers[name] is driver:
+                # This might happen example if we import a driver-class inside a plugin-file.
+                # It should be valid, example if you need it for typing.
+                return None
+            raise Exception(
+                f'Driver with this name ({name}) already exists. CaSe of driver is ignored.'
+            )
+        logging.debug(f'Registered driver {name}')
+        self.drivers[name] = driver
+
+    def _preload_drivers(self):
+        for name, values in config.OPTIONAL_COMPONENTS.items():
+            name = name.lower()
+            load = values.get('LOAD', 'auto')
+            if load == 'no':
+                continue
+
+            drivername = values.get('DRIVER')
+            try:
+                driver = self.drivers[drivername]
+            except KeyError:
+                raise Exception(
+                    f'Invalid driver specified ({drivername}), no way to handle it'
+                )
+
+            driverinstance = driver(opts=values.get('OPTS', {}), load=load)
+            driverinstance.pm = self
+            self.optional_components[name] = driverinstance
+
+            logging.info(
+                f'Connecting to {name} with driver {drivername}, using {driverinstance.opts}'
+            )
+            yield driverinstance
+
+    async def load_components(self):
+        for driverinstance in self._preload_drivers():
+            if asyncio.iscoroutinefunction(driverinstance.connect):
+                await driverinstance.initialize_async()
+            else:
+                driverinstance.initialize()
+
+    def load_sync_components_global(self):
+        for driverinstance in self._preload_drivers():
+            if asyncio.iscoroutinefunction(driverinstance.connect):
+                logging.debug(f'Driver {driverinstance.name} is async, wont load')
+            else:
+                driverinstance.initialize()
+
+    def register_hook_definition(self, obj):
+        try:
+            name = obj.name
+        except AttributeError:
+            name = obj.__name__
+
+        if name in self.hook_definitions:
+            raise Exception(
+                f'There can only be 1 hook-definition per hook-name. "{name}" is already registered'
+            )
+
+        self.hook_definitions[name] = obj
+
+    def register_hook(self, obj):
+        try:
+            name = obj.name
+        except AttributeError:
+            name = obj.__name__
+
+        try:
+            hook_definition = self.hook_definitions[name]
+        except KeyError:
+            raise Exception(
+                f'There are no hook-definition for hook "{name}", are you using the correct name?'
+            )
+
+        if hook_definition.is_async:
+            if not asyncio.iscoroutinefunction(obj.run):
+                raise Exception(
+                    f'Hook-definition is marked as async but the function is not ({obj.run}).'
+                )
+        else:
+            if asyncio.iscoroutinefunction(obj.run):
+                raise Exception(
+                    f'Hook-definition is not marked as async, but is.. Mark it as async or make it sync: {obj.run}'
+                )
+
+        self.hooks[name].append(obj())
+
+    def run_setup(self, obj, params):
+        params = filter_dict_to_function(params, obj.__init__)
+        name = f'{obj.__module__}.{obj.__name__}'
+        self.status[name]['init'] = obj(**params)
+
+    def call(self, name, *args, **kwargs):
+        func = self.hooks[name].run
+        if asyncio.iscoroutinefunction(func):
+            raise Exception(
+                f'The hook function ({func}) is async and should not be called using non-async calls. Call it using "await call_async()"'
+            )
+        return func(*args, **kwargs)
+
+    async def call_async(self, name, *args, **kwargs):
+        func = self.hooks[name].run
+        if not asyncio.iscoroutinefunction(func):
+            raise Exception(
+                f'The hook function ({func}) is not async call it using "call(..)"'
+            )
+
+        return await self.hooks[name].run(*args, **kwargs)
+
+
+def _get_plugindata():
+    """
+    Plugins are imported from multiple paths with these rules:
+      * First with a unique name wins
+      * There are multiple matchers, that ALL must return true. They return true if they are NOT set, or if they match "$plugin_path / $plugin_name"
+        * PLUGIN_WHITELIST_RE (regex)
+        * PLUGIN_WHITELIST_LIST
+        * PLUGIN_WHITELIST_TAGS
+        * not in PLUGIN_BLACKLIST_LIST
+        * not in PLUGIN_BLACKLIST_RE
+        * not in PLUGIN_BLACKLIST_TAGS
+    """
+    PLUGIN_WHITELIST_RE = re.compile(config.PLUGIN_WHITELIST_RE)
+    PLUGIN_BLACKLIST_RE = re.compile(config.PLUGIN_BLACKLIST_RE)
+
+    PLUGIN_WHITELIST_TAGS = set(config.PLUGIN_WHITELIST_TAGS)
+    PLUGIN_BLACKLIST_TAGS = set(config.PLUGIN_BLACKLIST_TAGS)
+
+    PLUGIN_PATHS = unique(
+        list([config.PLUGIN_PATHS])
+        if isinstance(config.PLUGIN_PATHS, str)
+        else config.PLUGIN_PATHS
+    ) + ['/data/opa/plugins']
+
+    logging.info(
+        'Plugin loading settings:'
+        f'  plugin-paths: {PLUGIN_PATHS}\n'
+        f'  whitelist-regex: {PLUGIN_WHITELIST_RE}\n'
+        f'  whitelist-list: {config.PLUGIN_WHITELIST_LIST}\n'
+        f'  whitelist-tags: {config.PLUGIN_WHITELIST_TAGS}\n'
+        f'  blacklist-list: {config.PLUGIN_BLACKLIST_LIST}\n'
+        f'  blacklist-regex: {PLUGIN_BLACKLIST_RE}\n'
+        f'  blacklist-tags: {PLUGIN_BLACKLIST_TAGS}\n'
+    )
+
+    sys_paths = sys.path + PLUGIN_PATHS
+    sys.path = unique(sys_paths)
+
+    plugins_to_load = defaultdict(list)
+    task_candidates = []
+    routers = []
+
+    for plugin in pkgutil.iter_modules(PLUGIN_PATHS):
+        allow_match = os.path.join(plugin.module_finder.path, plugin.name)
+        tasks_candidate = False
+
+        if plugin.ispkg:
+            metafile = os.path.join(allow_match, 'meta.json')
+
+            if os.path.exists(os.path.join(allow_match, 'tasks.py')):
+                tasks_candidate = True
+        else:
+            metafile = f'{allow_match}-meta.json'
+
+        logging.debug('')
+        logging.debug(f'Checking if we should load "{allow_match}"')
+
+        if os.path.exists(metafile):
+            logging.debug(f'Found metafile @ {metafile}')
+            metadata = json.load(open(metafile, 'r'))
+        else:
+            logging.debug(f'Metafile @ {metafile} does not exist, using empty metadata')
+            metadata = {}
+        logging.debug(f'Metadata: {metadata}')
+
+        load_checks = {}
+
+        if config.PLUGIN_WHITELIST_LIST:
+            load_checks['PLUGIN_WHITELIST_LIST'] = (
+                allow_match in config.PLUGIN_WHITELIST_LIST
+            )
+
+        if PLUGIN_WHITELIST_RE.pattern:
+            load_checks['PLUGIN_WHITELIST_RE'] = bool(
+                PLUGIN_WHITELIST_RE.match(allow_match)
+            )
+
+        if PLUGIN_WHITELIST_TAGS:
+            load_checks['PLUGIN_WHITELIST_TAGS'] = bool(
+                PLUGIN_WHITELIST_TAGS & set(metadata.get('tags', []))
+            )
+
+        if config.PLUGIN_BLACKLIST_LIST:
+            load_checks['PLUGIN_BLACKLIST_LIST'] = (
+                allow_match not in config.PLUGIN_BLACKLIST_LIST
+            )
+
+        if PLUGIN_BLACKLIST_RE.pattern:
+            load_checks['PLUGIN_BLACKLIST_RE'] = not bool(
+                PLUGIN_BLACKLIST_RE.match(allow_match)
+            )
+
+        if PLUGIN_BLACKLIST_TAGS:
+            load_checks['PLUGIN_BLACKLIST_TAGS'] = not bool(
+                PLUGIN_BLACKLIST_TAGS & set(metadata.get('tags', []))
+            )
+
+        load = all(load_checks.values())
+        logging.debug(f'Load-checks: {load_checks}, overall({load})')
+
+        if not load:
+            continue
+
+        logging.info(f'Loading plugin: {plugin.name}')
+        mod = import_module(plugin.name)
+
+        if tasks_candidate:
+            task_candidates.append(plugin.name)
+
+        defined_plugins = get_defined_plugins(mod)
+        for pt in ['hook-definitions', 'hooks', 'drivers', 'setup']:
+            plugins_to_load[pt] += defined_plugins[pt]
+        if hasattr(mod, 'router'):
+            routers.append(mod.router)
+
+    return {
+        'plugins_to_load': plugins_to_load,
+        'task_candidates': task_candidates,
+        'routers': routers,
+    }
+
+
+plugin_manager: PluginManager
+
+
+async def startup(app):
+    global plugin_manager
+    plugin_manager = PluginManager()
+
+    plugin_manager.store.update(**_get_plugindata())
+
+    for hook_definition in plugin_manager.store['plugins_to_load']['hook-definitions']:
+        plugin_manager.register_hook_definition(hook_definition)
+
+    for hook in plugin_manager.store['plugins_to_load']['hooks']:
+        plugin_manager.register_hook(hook)
+    plugin_manager.post_hook()
+
+    for driver in plugin_manager.store['plugins_to_load']['drivers']:
+        plugin_manager.register_driver(driver)
+    await plugin_manager.load_components()
+
+    for router in plugin_manager.store['routers']:
+        app.include_router(router)
+
+    for setup in plugin_manager.store['plugins_to_load']['setup']:
+        plugin_manager.run_setup(setup, {'app': app})
+
+
+def startup_simple():
+    """
+    Startup for simple sync apps that want some of the core functionality of opa-stack,
+    but not the fastapi app itself. Usefull for things like celery workers
+    """
+    global plugin_manager
+    plugin_manager = PluginManager()
+
+    plugin_manager.store.update(**_get_plugindata())
+
+    for hook_definition in plugin_manager.store['plugins_to_load']['hook-definitions']:
+        plugin_manager.register_hook_definition(hook_definition)
+
+    for hook in plugin_manager.store['plugins_to_load']['hooks']:
+        plugin_manager.register_hook(hook)
+    plugin_manager.post_hook()
+
+    for driver in plugin_manager.store['plugins_to_load']['drivers']:
+        plugin_manager.register_driver(driver)
+    plugin_manager.load_sync_components_global()
+
+
+async def shutdown():
+    pass
+
+
+def get_plugin_manager() -> PluginManager:
+    return plugin_manager
+
+
+def get_component(name: str):
+    return plugin_manager.optional_components[name]
+
+
+def get_instance(name: str):
+    return get_component(name).instance
+
+
+def get_util(name: str):
+    return get_component(name).util
+
+
+def call_hook(name, *args, **kwargs):
+    return plugin_manager.call(name, *args, **kwargs)
+
+
+async def call_hook_async(name, *args, **kwargs):
+    return await plugin_manager.call_async(name, *args, **kwargs)
+
+
+def get_router(*args, **kwargs) -> APIRouter:
+    return APIRouter(*args, **kwargs)
+

# Adding plugin-paths

To use a custom plugin-folder, populate the configuration named PLUGIN_PATHS with a comma-separated-list of path's.

TIP

The path /data/opa/plugins will always be appened, this is because we use plugins internally as well. However, they can easiely be overridden if wanted as they are appended as the last item in the list.

# File-structure

When loading plugins, we uses PLUGIN_PATHS to find possible plugins. We will load both plugins that are single files, but also packages containing an __init__.py.

WARNING

Make sure the name of your plugin is unique, since we are using the pythons import system when importing it. +Example.. Don't call your plugin redis.py and expect it to work... 😃 You will probably get some errors down the line.

The order when the plugins are loaded is important. If you want to load plugin myplugin, and there are 2 files with that name, the first one in the PLUGIN_PATHS will win. Just as in normal with PATH variables. +We will ONLY care about the first one. Even if the first one is going to get ignored by a filter (see below)

# Configuration

  • PLUGIN_PATH: List of paths to potentially load plugins from, default [], ie, only /data/opa/plugins (as it is always there..). +This is an array, and can be overwritten if you define it multiple places, to merge multiple entries, write, example (notice the dynacon_merge) (dynaconf-docs)
default:
+  PLUGIN_PATHS:
+    - "/extra_plugins"
+    - dynaconf_merge
+

TIP

If you want to define this value using an environment-variable, you can define it as a string (OPA_PLUGIN_PATHS='/plugins') or a list, (OPA_PLUGIN_PATHS='["/plugins", "/more_plugins"]')

If a plugin is loaded or not can be dictated using rules. There are many different filters, and they are all set to allow-as-default. +You should only use 0, 1 or maybe 2 of the settings.. But feel free to use as many as you like 😃

  • PLUGIN_WHITELIST_LIST (default: []): List of whitelisted plugins to load.

  • PLUGIN_WHITELIST_RE (default: ""): Regex of whitelisted plugins to load.

  • PLUGIN_WHITELIST_TAGS (default: []): List of whitelisted plugin tags to load. Setting this means that we will ONLY load plugins having one or more of these tags.

  • PLUGIN_BLACKLIST_LIST (default: []): List of blacklisted plugins to not load.

  • PLUGIN_BLACKLIST_RE (default: ""): Regex of blacklisted plugins to not load.

  • PLUGIN_BLACKLIST_TAGS (default: []): List of blacklisted plugin tags to not load.

The default settings means that ALL available plugins (in the paths) will be loaded by default. Which might be just what you want.

TIP

The _LIST and _RE matches will match against the plugin-path, and the module-name. Example-paths it will need to match against

  • /data/opa/demo-plugins/demo_noop: For the file inside /data/opa/demo-plugins named demo_noop.py
  • /data/opa/demo-plugins/demo_model: For the demo_model package (a folder with an __init__.py file)

The _TAGS matchers will check for metadata

+ + + diff --git a/guide/development.html b/guide/development.html new file mode 100644 index 0000000..304da36 --- /dev/null +++ b/guide/development.html @@ -0,0 +1,113 @@ + + + + + + Development | opa-stack + + + + + + + + +

# Development

# Commands

  • Getting a mongo-shell

    • docker-compose exec mongo mongo mongodb://mongo:mongo@localhost:27017/api
  • Stop api and run manually

    • s6-svc -dq /var/run/s6/services/api/
    • cd /data/ +
      • uvicorn opa.main:app --host 0.0.0.0
      • OPA_PLUGIN_PATHS=/opa-stack/examples/docker-compose/celery-task/plugins/ uvicorn opa.main:app --host 0.0.0.0 --log-level debug

# Resources

  • Cheatsheet - https://gitlab.com/euri10/fastapi_cheatsheet

# Builtin libs/utils

# Logging

  • from opa import log

The log object will behave just like a loguru object. In addition to use the log.info('...') and other log-function, you can also use it in after an object to get additional quick info.

Example

from opa import log
+
+log.info('Hello')
+
+a = {'b': [1, 2, 3]} | log  # Will output the object `a` as `DEBUG`.
+print(a['b'])  # [1, 2, 3]
+

# Development mode (ENV=dev)

  • Currently, only the api does something when ENV is set.. But this might change when more components are added later.

On dev, if you set ENV to dev, a few things happens, some of them are

# PTVSD

The port 5678 will be exposed, and you can connect example vscode to it so it can help you debug your code. +You will be able to set breakpoints, and do the regular debugging in vscode. This also works inside async code.

Example-configuration (launch.json) for vscode if you use docker-compose and expose the port like "127.0.0.1:5678:5678"

{
+    "version": "0.2.0",
+    "configurations": [{
+        "name": "Python: Remote Attach",
+        "type": "python",
+        "request": "attach",
+        "port": 5678,
+        "host": "localhost",
+        "pathMappings": [{
+            "localRoot": "${workspaceFolder}/api/data/opa",
+            "remoteRoot": "/data/opa"
+        }]
+    }]
+}
+

# Better exceptions

Get better exception output on the console. It does many things, but one very usefull thing is that it shows you the content of variables inside the different exception frames.

# Plugins

The plugin-system is powerfull, you can read about it here. +Since there is a lot to it for developing them, there is a dedicated page for info about it.

# Interactive shell

You can get an interactive shell in the context of opa-stack by using opa/scripts/shell.sh. It will start an ipython shell where the plugins are available.

Example (from a host-machine using docker-compose)

docker-compose exec -w /data api ./opa/scripts/shell.sh
+
+In [1]: from opa import get_instance
+
+In [2]: walrus = get_instance('walrus')
+
+In [3]: walrus
+Out[3]: Database<ConnectionPool<Connection<host=redis,port=6379,db=0>>>
+
+In [4]: walrus.get('test')
+
+In [5]: walrus.set('test', 1)
+Out[5]: True
+
+In [6]: walrus.get('test')
+Out[6]: b'1'
+
+ + + diff --git a/guide/examples.html b/guide/examples.html new file mode 100644 index 0000000..5ec87b3 --- /dev/null +++ b/guide/examples.html @@ -0,0 +1,330 @@ + + + + + + Examples | opa-stack + + + + + + + + +

# Examples

TIP

Read this first if you need a new project

Note that on all the examples, you will also get these url's

  • http://127.0.0.1:8001/docs - Swagger documentation
  • http://127.0.0.1:8001/redoc - Redoc documentation
  • http://127.0.0.1:8001/openapi.json - OpenAPI schema

TIP

The FastAPI have already made an extremely usefull documentation that you should consider reading as well. +Because it exists, this doc won't be about deep things you can do with FastAPI

# Hello world

The first example is a simple docker-compose setup where you will

  • Setup +
    • Uses docker-compose

    • Use only the api container

    • Expose output from a simple command on /hello

    • Mount the plugins from a local plugin-folder.

    • Things to try

      • http://127.0.0.1:8001/hello - The output of the route in hello.py

# Files

docker-compose.yaml
version: "3.7"
+
+services:
+  api:
+    image: opastack/api:latest
+    ports:
+      - "127.0.0.1:8001:8000"
+    environment:
+      OPA_PLUGIN_PATHS: "/plugins"
+    volumes:
+      - ./plugins:/plugins
+
plugins/hello.py
from opa import get_router
+
+router = get_router()
+
+
+@router.get("/hello")
+def return_string():
+    return 'Hello to you'
+

# Timekeeper

This example is still simple, but shows you how to do a little bit more, including a better development environment.

  • Setup

    • Uses docker-compose
    • Using only the api container
    • Expose output on /time that also accept some parameters
    • Expose output on /month/{month} that converts a number (1-12) to month-name. Crashes if it is not able to...
    • Expose output on /sleep-async/{seconds} that sleeps.. This is an async function
    • Expose output on /sleep-sync/{seconds} that sleeps.. This is not an async function..
    • Mount the plugins from a local plugin-folder, watches them for changes.
    • Sets the environment to DEV (see docs for more info) +
      • In addition to other things, enabling DEV gives some neat developing features.. Check here for more info how to leverage them
  • Things to try

    • http://127.0.0.1:8001/time - Should show you the time in the default format
    • http://127.0.0.1:8001/docs#/timekeeper/get_time_time_get > Try it out > change format > Execute
    • http://127.0.0.1:8001/month/2
    • http://127.0.0.1:8001/month/20 +
      • See the better-exception (with variable output) in the console.
      • Try setting up vscode debugging and set a breakpoint and so on..
    • Hammer on http://127.0.0.1:8001/sleep-async/10 and http://127.0.0.1:8001/sleep-sync/20 on multiple terminals.. See how async is the king when it comes to things that blocks.
    • Or use something like hey, for better benchmarking. +
      • hey -n 100 -c 100 http://127.0.0.1:8001/sleep-async/5
      • hey -n 100 -c 100 http://127.0.0.1:8001/sleep-sync/5

# Files

docker-compose.yaml
version: "3.7"
+
+services:
+  api:
+    image: opastack/api:latest
+    ports:
+      - "127.0.0.1:8001:8000"
+      - "127.0.0.1:5678:5678"
+    environment:
+      OPA_PLUGIN_PATHS: "@merge /plugins"
+      OPA_PLUGIN_BLACKLIST_LIST: '["/data/opa/plugins/core-selfhosted"]'
+      ENV: "dev"
+      OPA_LOGLEVEL: "debug"
+    volumes:
+      - ./plugins:/plugins
+
plugins/timekeeper.py
import logging
+import datetime
+import secrets
+
+from time import sleep
+from asyncio import sleep as async_sleep
+
+from opa import get_router
+
+router = get_router()
+
+# Using tags helps separate your items from the also included demo-plugins
+@router.get("/time", tags=["timekeeper"])
+def get_time(format: str = '%Y-%m-%d %H:%M:%S'):
+    return datetime.datetime.now().strftime(format)
+
+
+@router.get("/month/{month}", tags=["timekeeper"])
+def get_monthname(month: int):
+    """
+    Make sure you give me a valid month number, or I might crash...
+    """
+    return datetime.datetime.strptime(str(month), '%m').strftime('%B')
+
+
+@router.get("/sleep-async/{seconds}", tags=["timekeeper"])
+async def async_sleeper(seconds: int):
+    """
+    I sleep for some seconds, then return how long I slept.. Neat!
+    But I'm also async, so it shoulnt block anything..
+    """
+    randstring = secrets.token_urlsafe(5)
+    logging.info(f'Start async sleep for ({randstring}) for {seconds}')
+    await async_sleep(seconds)
+    logging.info(f'Ending async sleep for ({randstring}) for {seconds}')
+    return f'I slept for {seconds} seconds'
+
+
+@router.get("/sleep-sync/{seconds}", tags=["timekeeper"])
+def sync_sleeper(seconds: int):
+    """
+    I'm almost like sleep-async, but I'm not async.. That means that I block python when I do nothing.
+    """
+    randstring = secrets.token_urlsafe(5)
+    logging.info(f'Start sync sleep for ({randstring}) for {seconds}')
+    sleep(seconds)
+    logging.info(f'Ending sync sleep for ({randstring}) for {seconds}')
+    return f'I slept for {seconds} seconds'
+

# Redis

Example using redis (both async and normal), using two different libs (aioredis and walrus), see dependencies for more info.

  • Setup

    • Uses docker-compose
    • Using the api container, and a redis-container
    • Expose output on /counter-async increments when you visit using aioredis
    • Expose output on /counter-sync increments when you visit using walrus
    • Expose output to GET and POST on /bloom, one to add entries to a bloom-filter (using walrus) and one to check. Walrus have a ton of neat feature, this is one of them.
  • Things to try

    • http://127.0.0.1:8001/counter-async - See a counter
    • cat a_file.md | curl -X POST -d @- "http://localhost:8000/bloom" - fill up a bloom filter
    • http://localhost:8000/bloom?string=sleep - Check for sleep in the bloomfilter

# Files

docker-compose.yaml
version: "3.7"
+
+services:
+  api:
+    # image: opastack/api:latest
+    image: 25724b069f78
+    ports:
+      - "127.0.0.1:8001:8000"
+    environment:
+      OPA_PLUGIN_PATHS: "/plugins"
+    volumes:
+      - ./plugins:/plugins
+  redis:
+    image: "redis:5"
+
plugins/timekeeper.py
from opa import get_instance, get_router
+
+router = get_router()
+
+
+@router.get("/counter-async")
+async def counter_async(key=None):
+    counter = await get_instance('aioredis').incr(key or 'incr-async')
+    return f'Counter is {counter}'
+
+
+@router.get("/counter-sync")
+def counter_sync(key=None):
+    counter = get_instance('walrus').incr(key or 'incr-sync')
+    return f'Counter is {counter}'
+
+
+@router.get("/bloom")
+def check_bloom_filter(string: str):
+    walrus = get_instance('walrus')
+    bf = walrus.bloom_filter('bf')
+    return string in bf
+
+
+@router.post("/bloom")
+def add_bloom_filter(string: str):
+    # Waiting for https://github.com/tiangolo/fastapi/issues/1018 to have plain/text input
+    # Possible now, but not with generation of the openapi spec..
+    walrus = get_instance('walrus')
+    bf = walrus.bloom_filter('bf')
+    for i in string.split(' '):
+        bf.add(i)
+
+    return f'Added entries'
+

# Background tasks

Background tasks are built into FastAPI/Starlette. If you come from the sync-world, you are probably used to Celery. +Celery complicates a lot, so if you just got a simple fire-and-forget job. A background-task might fit your needs.

See below for info about running a celery task.

  • Setup

    • Uses docker-compose
    • Using the api container
    • Uses redis for locking and keeping track of a string (not needed for running tasks where you don't care about results)
    • Runs background tasks using FastAPI's starlette background tasks
    • Let you POST to /runone to put a task to the queue, it runs for 4 seconds
    • Expose output on /runone to see status of which task is currently running
  • Things to try

    • Terminal 1 - Status +
      • while true; do curl http://localhost:8001/runone; echo; sleep 1; done
    • Terminal 2 - Trigger new tasks, try triggering many, see they queue up on terminal 1 +
      • curl -X POST http://localhost:8001/runone

# Files

docker-compose.yaml
version: "3.7"
+
+services:
+  api:
+    image: opastack/api:latest
+    ports:
+      - "127.0.0.1:8001:8000"
+    environment:
+      OPA_PLUGIN_PATHS: "/plugins"
+    volumes:
+      - ./plugins:/plugins
+  redis:
+    image: "redis:5"
+
plugins/tasks.py
from time import sleep
+from secrets import token_urlsafe
+
+from fastapi import BackgroundTasks, APIRouter
+
+from opa import get_router, get_instance
+
+router = get_router()
+
+
+def queuer(text: str):
+    walrus = get_instance('walrus')
+    lock = walrus.lock('runone')
+
+    with lock:
+        walrus.set('runone', text)
+        sleep(4)
+        walrus.delete('runone')
+
+
+@router.post("/runone")
+async def runone_post(background_tasks: BackgroundTasks):
+    random_str = token_urlsafe(5)
+    background_tasks.add_task(queuer, random_str)
+    return {"message": f"Triggered background task: {random_str}"}
+
+
+@router.get("/runone")
+async def runone_get():
+    walrus = get_instance('walrus')
+    return {"current_task": walrus.get('runone')}
+

# Celery task

Celery is a very powerfull distributed task-queue. It is much more feature-rich than the default background tasks available in FastAPI/Starlette.

  • Setup

    • Uses docker-compose
    • Same container image, but different config for +
      • api container
      • worker-math - Runs celery and consumes the math queue
      • worker-counter - Runs celery and consumes the counter queue
    • Redis for storing task-results
    • Rabbitmq for keeping track of tasks (broker)
    • Flower to see statuses
  • Things to try

    • http://localhost:8001/inc/5 - Hit the url a couple of times +
      • They will queue up. At the end of each task, the celery worker (in the worker-counter container) will increment a counter
      • The same counter is reported as output when accessing this url, since the api can access the same redis instance as the worker
    • http://localhost:8001/div/A/B - Hit the other worker (worker-math) +
      • http://localhost:8001/div/2/1 - normal queuing
      • http://localhost:8001/div/2/0 - one that will get an error
    • See tasks as they arrive +
      • Using celery events (doesnt mather which) +
        • docker-compose exec -w /data worker-counter celery -A opa.main events
        • docker-compose exec -w /data worker-math celery -A opa.main events
      • Using flower +
        • http://localhost:5555/
    • http://localhost:8001/inc/100 - Queue a task which takes 100 seconds +
      • Take a note of the task_id in the output
    • http://localhost:8001/status/27deec1a-c8f0-4d0d-95dc-59c6dd937207 - Check status for task +
      • The inc task updates some meta-info that we can query using another endpoint
      • There are also a background task that will get those updates in the fastapi context, see the __init__.py file for info
    • http://localhost:8001/last_status - Another way to get the status
  • Files at github: https://github.com/opa-stack/opa-stack/tree/master/examples/docker-compose/celery-task

docker-compose.yaml
version: "3.7"
+
+services:
+  api:
+    image: opastack/api:latest
+    ports:
+      - "127.0.0.1:8001:8000"
+    environment:
+      OPA_PLUGIN_PATHS: "/plugins"
+    volumes:
+      - ./plugins:/plugins
+
+  redis:
+    image: "redis:5"
+
+  rabbitmq:
+    image: "rabbitmq:3.8-management"
+    ports:
+      - "15672:15672" # Management port
+
+  flower:
+    image: "mher/flower:latest"
+    command: "-A proj --broker=amqp://guest:guest@rabbitmq:5672//"
+    ports:
+      - "5555:5555"
+
plugins/celerydemo/__init__.py
from opa import get_router, Hook, get_instance, app
+
+
+class celery_config(Hook):
+    name = 'driver.celery.setup'
+
+    def run(self, celery_app, task_candidates):
+        celery_app.conf.task_routes = {"worker.celery_worker.test_celery": "test-queue"}
+        celery_app.conf.update(task_track_started=True)
+        celery_app.autodiscover_tasks(task_candidates)
+        return celery_app
+
+
+router = get_router()
+
+
+@router.get("/add/{num1}/{num2}")
+async def root(num1: int, num2: int):
+    from celerydemo.tasks import test_celery
+
+    celery = get_instance('celery')
+    walrus = get_instance('walrus')
+    test_celery.delay('abc')
+    count = str(walrus.get('celery'))
+    return {"message": "Word received", 'count': count}
+
plugins/celerydemo/tasks.py
from time import sleep
+
+from celery import current_task
+
+from opa import get_instance
+
+celery = get_instance('celery')
+
+@celery.task
+def test_celery(word: str) -> str:
+    for i in range(1, 4):
+        sleep(1)
+        current_task.update_state(state='PROGRESS',
+                                  meta={'process_percent': i*10})
+
+    get_instance('walrus').incr('celery')
+    return f"test task return {word}...."
+ + + diff --git a/guide/index.html b/guide/index.html new file mode 100644 index 0000000..c9879a1 --- /dev/null +++ b/guide/index.html @@ -0,0 +1,73 @@ + + + + + + Intro | opa-stack + + + + + + + + +

# Intro

opa-stack is composed of multiple parts, where the most interesting part is the api +The api is the heart! See what it is build of here +There are other components as well, but they are mostly just support for the api.

opa-stack isn't just something you deploy and it's ready to use. It's a stack to build your own stuff on, but with it, a lot of batteries. 😃

To read more generic info about the project. Like project-goals, see the more-info page.

# Getting started

The easiest way to get started is reading some of the guides. They are good examples for different use-cases. +After some skimming through the content, then read more about the api.. Specially the plugin-system, as it will go into more details how it all fits together.

+ + + diff --git a/guide/new-project.html b/guide/new-project.html new file mode 100644 index 0000000..f82aa60 --- /dev/null +++ b/guide/new-project.html @@ -0,0 +1,76 @@ + + + + + + New project | opa-stack + + + + + + + + +

# New project

The only sane way to run the opa-stack is using containers and it is build using containers in mind from the very beginning. +There are multiple ways to run it, so choose whatever orchestrator fits you best below.

There are also some optional components you can use together with opa-stack if you want caching, db or and so on.

# Orchestrators

# docker-compose

Docker-compose is the simplest of the container orchestrators. You can download it from here, and read the getting started guide here.

Take a look at the simplest example project here. +In the folder just run docker-compose up, and you got things running.. See the docker-compose docs for more info.

# kubernetes (helm)

Nothing here yet...

+ + + diff --git a/guide/optional-components-reference.html b/guide/optional-components-reference.html new file mode 100644 index 0000000..1d674e3 --- /dev/null +++ b/guide/optional-components-reference.html @@ -0,0 +1,182 @@ + + + + + + Optional-components reference | opa-stack + + + + + + + + +

# Optional-components reference

If you havent read it, you should read about plugins first

# Configuration

TIP

You must use dynaconf merge when changing these parameters.. Example

myenv:
+  OPTIONAL_COMPONENTS:
+    dynaconf_merge: true
+    WALRUS:
+      LOAD: "auto"
+      DRIVER: "redis-walrus"
+      OPTS:
+        URL: "redis://redis"
+

Take a look at the default configuration here or see the file below (it is under the OPTIONAL_COMPONENTS key) for some examples.

default-settings.yaml
default:
+  MODE: "prod"
+  PROJECT_NAME: "opa-stack"
+  PROJECT_DESCRIPTION: ""
+  PROJECT_VERSION: "0.1.0"
+
+  # Urls to automatic documentation. Set to null to disable
+  DOCS_URL: "/docs"
+  REDOC_URL: "/redoc"
+  OPENAPI_URL: "/openapi.json" # Can only be null if DOCS_URL and REDOC_URL is also null
+
+  OPENAPI_PREFIX: ""
+
+  PLUGIN_PATHS: []
+
+  PLUGIN_WHITELIST_LIST: []
+  PLUGIN_WHITELIST_RE: ""
+  PLUGIN_WHITELIST_TAGS: []
+  PLUGIN_BLACKLIST_LIST: []
+  PLUGIN_BLACKLIST_RE: ""
+  PLUGIN_BLACKLIST_TAGS: []
+
+  # CORS
+  ALLOW_ORIGINS: ["*"]
+  ALLOW_CREDENTIALS: true
+  ALLOW_METHODS: ["*"]
+  ALLOW_HEADERS: ["*"]
+
+  SECRET_KEY: ""
+
+  # Configuration for optional components..
+  # They all have a helper-file in ../utils/{component_name.lower()}.py, so check
+  # in them if you wonder how these values are used. They are also listed in the docs
+  # @ https://opa-stack.github.io/guide/components.html
+  #
+  # The LOAD option is always present, and can be one of auto|yes|no, all defaulting to 'auto'.
+  #   * auto: Makes opa-stack look for the component using a simple dns-check or other simple checks
+  #           Ie.. Check if we "should" be able to use this component.
+  #           It will then try to connect as if you had written "yes", ie, it fails if it is not able to..
+  #   * yes: Makes the coponent required
+  #   * no: Makes it not check at all.
+
+  OPTIONAL_COMPONENTS:
+    MONGODB:
+      LOAD: "auto"
+      DRIVER: "mongodb-async-motor"
+      OPTS:
+        URL: "mongodb://mongo:mongo@mongo:27017/opa"
+
+    WALRUS:
+      LOAD: "auto"
+      DRIVER: "redis-walrus"
+      OPTS:
+        URL: "redis://redis"
+
+    AIOREDIS:
+      LOAD: "auto"
+      DRIVER: "redis-aioredis"
+      OPTS:
+        URL: "redis://redis"
+
+    CELERY:
+      LOAD: "auto"
+      DRIVER: "celery"
+      OPTS:
+        BACKEND_URL: "redis://redis/1"
+        BROKER_URL: "pyamqp://guest@rabbitmq//"
+
+  # Used different places, currently:
+  #  * Setting FastAPI/Starlette debug-mode (https://www.starlette.io/applications/)
+  DEBUG: false
+
+  # Turns on better exceptions, ie, prettier, and with some more info (like variables)
+  BETTER_EXCEPTIONS: false
+
+  # Python Tools for Visual Studio debug server, running on port 5678
+  PTVSD: false
+
+  # External js files are loaded from the internet as default
+  SELF_HOSTED: false
+
+dev:
+  MODE: "dev"
+  PTVSD: true
+  BETTER_EXCEPTIONS: true
+  BETTER_EXCEPTIONS_MAX_LENGTH: 1000
+  PLUGIN_PATHS:
+    - "/data/opa/demo-plugins"
+    - dynaconf_merge
+

# Usage

When you want to use an optional-component you the best way is to use opa.get_instance(name) like this

from opa import get_instance, get_router
+from opa.plugins.driver_redis import Walrus
+
+router = get_router()
+
+@router.get("/")
+def counter_sync():
+    walrus = get_intance('walrus')
+    counter = walrus.incr('counter')
+    return f'Counter is {counter}'
+

WARNING

opa.get_instance() takes the lowercase component key-name as input

The other ways to get the instance (might be usefull in some cases)

  • Use component = opa.get_component(name)
    • The instance will be under component.instance, but there are other component-info you might want under component
  • Use pm = opa.get_plugin_manager(), then pm.optional_components[name] to get the component instance

# Drivers

Backend Library Drivername Async
Redis Aioredis redis-aioredis yes
Redis Walrus redis-walrus no
Mongodb Motor mongodb-async-motor yes
Celery Celery celery no

# Redis

# Aioredis

# Walrus

  • Very powerful redis library with support for a lot of additional features

# Mongodb

# Motor

# Celery / tasks

If you need basic background tasks, look into FastAPI/Starlette BackgroundTasks instead.

There is builtin support for running celery tasks, see the sample-project for details, view info about the sample project here

The opa-stack's api-container can run in worker mode, ie, it will run a celery worker with the same codebase as the rest of the api. This is by far the simplest setup, but feel free to run your own workers if you need.

  • Set environment-variable MODE=worker to start it as a worker
  • Set additional celery params (celery worker -A opa.main ${CELERY_PARAMS})

Some important notes about using celery in opa-stack is:

  • The tasks must be defined in tasks.py, ie, you will need your plugin to be a python package (folder with __init__.py).
  • You can access optional-components as usual in the tasks, but only non-async drivers are working. Ie, you can't use drivers like motor or aioredis in your tasks. +
    • It is important that you DONT import any of your tasks on top of the file where your route is defined. Ie, do the from packagename.tasks import sometask inside your @router.get('/')... function!
  • The celery worker is using most of the opa-stack code. So configuration, driver-loading, hooks and so on will also work in the celery worker.
+ + + diff --git a/guide/plugins.html b/guide/plugins.html new file mode 100644 index 0000000..114af53 --- /dev/null +++ b/guide/plugins.html @@ -0,0 +1,253 @@ + + + + + + Plugins | opa-stack + + + + + + + + +

# Plugins

A plugin is how you make things using opa-stack. +They are just python packages or modules available to opa-stack. See general info about the plugin-system to see how it works.

# Types of plugins

Different types of plugins are initialized in different order. Example, all hooks are initialized before drivers are initialized. Drivers are done when setup is run.

# Importables

Every PLUGIN_PATHS are loaded into pythons sys.path and imported. Therefor, you can also have plugins that exposes functionality to other plugins.

So, if you got

  • /plugins/common/math_utils.py with a function double()
  • /plugins/secure/grade_calculations.py that adds a route. It can use from math_utils import double without problems

# Hook Definition

Hooks need to be defined before you can use them, so for this, you need a hook-definition.

A simple example looks like

from opa.core.plugin import HookDefinition
+
+class name_hook(HookDefinition):
+    required = True
+    is_async = False
+    name = 'fullname'
+

Parameters:

  • required (default: False): Set to True if you don't want the application to be able to start unless there is a hook for this definition.
  • is_async (default: False): Set to True if the hook should be an async function.
  • name (default: function-name): Override the function-name as the name of the hook.

# Hook

Hooks are used if you want to replace or provide a custom function for a part of your code.

Example, think of a tiny generic app like this:

from opa.core.plugin import (
+    get_plugin_manager,
+    get_router,
+    call_hook,
+    Hook
+)
+
+router = get_router()
+
+
+@router.get("/get-fullname/{firstname}/{surname}")
+def show_name(
+    firstname: str, surname: str
+):
+    fullname = call_hook('fullname', firstname, surname)
+    return {'name': fullname}
+

It calculates fullname based on whatever the hook called fullname returns. +The fullname is already defined above in HookDefinition, so now lets define the function itself in a completly different plugin.

from opa.core.plugin import Hook
+
+class fullname_hook(Hook):
+    name = 'fullname'
+    order = 1
+
+    def run(self, firstname, lastname):
+        return f'{firstname} {lastname}'
+

Some key takeaways:

  • If the hook-definition's is_async is True, you will get an error if the run function is not async (async def run(...))
  • call_hook is for sync, there is also a call_hook_async for async
  • Whatever arguments that the call-functions gets are what the run functions will get.
  • If there are multiple hooks for a single definition, the one with the highest order wins (default order is 0).
  • The HookDefinition and the route that calls call_hook are usually together in the same plugin. The hooks are normally separate plugins.
  • A typical usecase is to also define a Hook with default order of -1 togehter with the HookDefinition, that way, your pm.call will have a default hook
  • There can only be 1 hook definition per name or you will get an error.

Complete example

Now, this was a really simple example. But it can be used for many things

  • Language or environment specific code (load different hook-plugins based on tags)
  • A hierarcy of internal plugins, where you define some of their behavior dynamicly using a simple environment variable to choose an env
  • Provide additional info if running in dev-env
  • Provide a solid base, for others to built small custom plugins on

# Drivers / Optional-components

More than often, you will need a database, cache backend (redis) or another 3rd party component. +You can use one of the built-in drivers, or you can make your own very easiely.

# Configuration

The default configuration loads a couple of drivers if it can find the component. That means that, if opa-stack think redis is available, it will try to connect to it. The redis drivers just uses a simple host-lookup for this check (check if redis avalable).

All drivers are configured under the OPTIONAL_COMPONENTS in the configuration. The default configuration is here, or see below.

docker-compose.yaml
default:
+  MODE: "prod"
+  PROJECT_NAME: "opa-stack"
+  PROJECT_DESCRIPTION: ""
+  PROJECT_VERSION: "0.1.0"
+
+  # Urls to automatic documentation. Set to null to disable
+  DOCS_URL: "/docs"
+  REDOC_URL: "/redoc"
+  OPENAPI_URL: "/openapi.json" # Can only be null if DOCS_URL and REDOC_URL is also null
+
+  OPENAPI_PREFIX: ""
+
+  PLUGIN_PATHS: []
+
+  PLUGIN_WHITELIST_LIST: []
+  PLUGIN_WHITELIST_RE: ""
+  PLUGIN_WHITELIST_TAGS: []
+  PLUGIN_BLACKLIST_LIST: []
+  PLUGIN_BLACKLIST_RE: ""
+  PLUGIN_BLACKLIST_TAGS: []
+
+  # CORS
+  ALLOW_ORIGINS: ["*"]
+  ALLOW_CREDENTIALS: true
+  ALLOW_METHODS: ["*"]
+  ALLOW_HEADERS: ["*"]
+
+  SECRET_KEY: ""
+
+  # Configuration for optional components..
+  # They all have a helper-file in ../utils/{component_name.lower()}.py, so check
+  # in them if you wonder how these values are used. They are also listed in the docs
+  # @ https://opa-stack.github.io/guide/components.html
+  #
+  # The LOAD option is always present, and can be one of auto|yes|no, all defaulting to 'auto'.
+  #   * auto: Makes opa-stack look for the component using a simple dns-check or other simple checks
+  #           Ie.. Check if we "should" be able to use this component.
+  #           It will then try to connect as if you had written "yes", ie, it fails if it is not able to..
+  #   * yes: Makes the coponent required
+  #   * no: Makes it not check at all.
+
+  OPTIONAL_COMPONENTS:
+    MONGODB:
+      LOAD: "auto"
+      DRIVER: "mongodb-async-motor"
+      OPTS:
+        URL: "mongodb://mongo:mongo@mongo:27017/opa"
+
+    WALRUS:
+      LOAD: "auto"
+      DRIVER: "redis-walrus"
+      OPTS:
+        URL: "redis://redis"
+
+    AIOREDIS:
+      LOAD: "auto"
+      DRIVER: "redis-aioredis"
+      OPTS:
+        URL: "redis://redis"
+
+    CELERY:
+      LOAD: "auto"
+      DRIVER: "celery"
+      OPTS:
+        BACKEND_URL: "redis://redis/1"
+        BROKER_URL: "pyamqp://guest@rabbitmq//"
+
+  # Used different places, currently:
+  #  * Setting FastAPI/Starlette debug-mode (https://www.starlette.io/applications/)
+  DEBUG: false
+
+  # Turns on better exceptions, ie, prettier, and with some more info (like variables)
+  BETTER_EXCEPTIONS: false
+
+  # Python Tools for Visual Studio debug server, running on port 5678
+  PTVSD: false
+
+  # External js files are loaded from the internet as default
+  SELF_HOSTED: false
+
+dev:
+  MODE: "dev"
+  PTVSD: true
+  BETTER_EXCEPTIONS: true
+  BETTER_EXCEPTIONS_MAX_LENGTH: 1000
+  PLUGIN_PATHS:
+    - "/data/opa/demo-plugins"
+    - dynaconf_merge
+
# Simple example
myenv:
+  OPTIONAL_COMPONENTS:
+    dynaconf_merge: true
+    MYREDIS:
+      LOAD: "auto"
+      DRIVER: "redis-walrus"
+      OPTS:
+        URL: "redis://some-external-redis"
+

In the example above:

  • It will use the redis-walrus driver
  • Give you an instance of it named myredis available in your plugins
  • Load if the hostname some-external-redis answers
  • Merge with the other optional-components already defined (dynaconf_merge)

# Using

To use an optional-component (lets say the one defined above), load it as a dependency in your route, like

@router.get("/counter")
+def counter_sync():
+    myredis = get_instance('myredis')
+    counter = myredis.incr('counter')
+    return f'Counter is {counter}'
+

Note that myredis is the component itself, not just the instance. There might be other functions available, like utility-functions.

Take a look at the optional-components reference for a list of all builtin drivers/optional-components

# Adding a driver

You can add a driver using a plugin. For probably the best example, see the driver-redis plugin at github (or below).

driver_redis.py
import logging
+import aioredis as aioredislib
+import walrus as walruslib
+
+from opa import Driver
+from opa.utils import host_exists
+
+
+class Aioredis(Driver):
+    name = 'redis-aioredis'
+
+    async def connect(self):
+        if not host_exists(self.opts.URL, 'database-url'):
+            return False
+
+        self.instance = await aioredislib.create_redis_pool(self.opts.URL)
+
+    async def disconnect(self):
+        self.instance.close()
+        self.instance.wait_closed()
+
+
+class Walrus(Driver):
+    name = 'redis-walrus'
+
+    def connect(self):
+        if not host_exists(self.opts.URL, 'database-url'):
+            return False
+
+        self.instance = walruslib.Database.from_url(self.opts.URL)
+
+    def validate(self):
+        self.instance.client_id()
+

In driver_redis you will see that a driver is just a class inherited from opa.Driver:

  • pm is the plugin-manager instance, that is available as self.pm inside the driver-instance
  • opts will be populated with the original OPTS for this driver
  • connect()
    • Can by async or sync.
    • Is run when we load the application.
    • opts is the opts configured, example opts.URL
    • If the configuration LOAD is set to auto
      • This function needs to return False to signal that the pre-check (ie, hostname check) failed and it should not connect.. If it does not return False, we will validate try to validate the connection even if LOAD=auto
    • Is responsible for setting self.instance.
  • validate(): +
    • Needs to be async if connect is async
    • Will run only if +
      • LOAD config is yes
      • or if connect did not return False
    • Should raise an exception of any kind if it is not able to validate the connection.
  • disconnect(self): Not implemented yet
  • get_instance(self): Normally it just returns self.instance, but if you want, you can override it

TIP

If you want some logic in your driver, you can use hooks, just use scall_hook or call_hook_async as described above.

# API's and routes

If you want to add a route accessible via an api, there are multiple ways to do it.

Expose an APIRouter instance named router in your plugin is probably the easiest. It will work for most of the usecases.

from opa import get_router
+router = get_router()
+
+@router.get("/")
+def root():
+    return 'Hello'
+

TIP

The get_router function is nothing magical, it is basicly the same as fastapi.APIRouter().

Another way is to use the Setup plugin. It will get the main fastapi app object as input, which you can use to add a route or do more powerfull things.

# Metadata

A plugin can have metadata attached to it in form of a json file.

  • If it's a flat .py file. Create a file with -meta.json
    • Exammple on my_utils.py you will have my_utils-meta.json
  • If it is a python package, make /meta.json in the same folder as the package.

Currently, you can only define tags as metadata, which can be used for filtering (PLUGIN_WHITELIST_TAGS and PLUGIN_BLACKLIST_TAGS) which plugins to load.

Example

{
+  "tags": ["utils", "admin"]
+}
+

# Examples

+ + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..d20d5fb --- /dev/null +++ b/index.html @@ -0,0 +1,74 @@ + + + + + + opa-stack + + + + + + + + +

+ OPA-stack +

+ Ops (API|Automation) stack +

+ Get Started → +

Powerfull base

A powerfull base with the stack you need.

Cloud-native ready

Based on microservices, so it is ready for your kubernetes cluster

Customizable

Does little out of the box, but gives you the stack you can build on.

WARNING

This prosject is very young and might not be usable just yet... Give feedback!

If you want to know more about the project goals before diving in, take a look at the more info page

Status
App api container Docker Pulls Docker Image Version (latest semver) GitHub issues GitHub pull requests GitHub GitHub last commit codecov travis
Docs GitHub issues GitHub pull requests GitHub last commit travis
+ + + diff --git a/more-info/contact.html b/more-info/contact.html new file mode 100644 index 0000000..8df0e3f --- /dev/null +++ b/more-info/contact.html @@ -0,0 +1,70 @@ + + + + + + Contact | opa-stack + + + + + + + + +

# Contact

  • Issues: https://github.com/opa-stack/opa-stack/issues
  • Pull-requests: https://github.com/opa-stack/opa-stack/pulls
  • Security-issues: @xeor at keybase
  • Other: opa@boa.nu
+ + + diff --git a/more-info/index.html b/more-info/index.html new file mode 100644 index 0000000..5d6c3c4 --- /dev/null +++ b/more-info/index.html @@ -0,0 +1,73 @@ + + + + + + More info | opa-stack + + + + + + + + +

# More info

# Project goal

Working as an engineer/devop-person for some years, one thing often missing is a home for your code doing very custom things, specially logic you often want in the middle of other API's.

The requirements was therefor more or less:

  • Need for python3, non-blocking, async and modern
  • A place to store non-relational temporary (or semi-permanent) data
  • Job queue for things that might take time or you want custom schedule
  • Good self-documenting api endpoints for building other tools on. rest, graphql and websocket out of the box
  • Be dynamic enough to support multiple instances of it running, with some optional communication between them. (Example for different security contexts)
  • Good support for an out-of-the-box k8s deployment, including scalability
  • A way to do almost everything via plugins
  • An optional component system where you can just attach some additional services, like redis, and have it working

# FAQ

# Why not use something out there? Example node-red..

There are a lot of nice tooling out there that solves similar problems. But I really coulnt find anything that was dynamic enough to support what I wanted/needed.

# But what about just setting up everything yourself..?

That is just what this project is trying to do, but doing it in a way so you can use plugins with your actual code, without needing to setup a whole stack. opa-stack tries to gives you a modern base, with minimal efford to start coding.

Take a look at what it's made of below for information what this stack is made of

# But... Then what about other project templates, cookiecutter and other examples

Fair point.. There are a lot of good start-templates out there, both for FastAPI and cookiecutter. +And in many cases, they might be better. Example if you are making a big website, or a custom api for a chat platform (or ...).

What the opa-stack gives you is a powerfull platform for where you can do very custom things, but as plugins. Those plugins have access to things they need to be a good api, and to talk to other api's.

# When should I not use opa-stack?

Hard to say, but I woulnt want to use it if the project will have dedicated people working on it, then it might be a good thing starting from scratch, or maybe from a cookiecutter template.

Feel free to use it as an api-endpoint for other parts of your project tho. Let say your main work is on the frontend, but you need an api for something custom..

# When should I use the opa-stack?

Read some docs, see if it fits. If you are a team that needs to talk to a bunch of api's and do custom things using python, it is probably a good fit.

Or maybe you have a team of 20 people, all going to eventually wanting to do a little thing in python where they want access to it via an api, or any other channel.

Opa-stack might also be a good fit if you want a python engine that does something, if something.. You don't haveto use the api-part 😃

# Whats about the name?

The OP in OPA stands for ops, as in a group of people/mindset (yes, yes.. dev is not a part of the name, but it's kinda implicit given the whole name.. kinda 😃).

The A is for API or Automation, or both. You choose 😏

# What it's made of

+ + + diff --git a/opa-stack/index.html b/opa-stack/index.html new file mode 100644 index 0000000..5e7d3e0 --- /dev/null +++ b/opa-stack/index.html @@ -0,0 +1,66 @@ + + + + + + opa-stack + + + + + + + + + + + +