unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
8 lines (7 loc) • 13 kB
JavaScript
import{E as B,j as t,F as U,A as L,l as M,C as P,m as K,n as W,bf as Q,kF as X,o as Z,b as ee,T as E,s as w,u as te,b7 as se,r as v,b6 as N,b8 as V,ba as ae,c0 as z,aN as G,by as ne,hP as re,bA as oe,f as R,bB as le,b9 as ie,bq as ce,ao as de,kG as ue,bG as pe,kH as he,b5 as D,kI as ge,jR as me,jH as fe,i as be,h as k,kJ as ve,cp as ye,aQ as je,aR as Se,aO as Te,aU as H,be as q,bc as J,kK as xe,kL as Ee,kM as Ce,jF as ke,kN as Re,B as Ae,an as Ie,cq as Oe,fU as De,hg as Pe}from"./index-Cikp5fMR.js";var _={},we=U;Object.defineProperty(_,"__esModule",{value:!0});var Y=_.default=void 0,_e=we(B()),$e=t,Fe=(0,_e.default)((0,$e.jsx)("path",{d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"}),"IndeterminateCheckBox");Y=_.default=Fe;const qe=X(),pt=({options:e,selectedOptions:a,indeterminateOptions:s,tagType:i,existingTags:d,disabled:l=!1,onChange:r})=>{const c=t.jsx(Z,{fontSize:"small"}),u=p=>p.inputValue?p.inputValue:p.title,g=({key:p,...h},o,{selected:b})=>{const S=(s==null?void 0:s.some(j=>j.title===o.title))??!1;return t.jsxs("li",{...h,children:[t.jsx(P,{condition:!!o.inputValue,show:t.jsx(Q,{sx:{mr:j=>j.spacing(.5)}}),elseShow:t.jsx(K,{icon:c,checkedIcon:t.jsx(W,{fontSize:"small"}),indeterminateIcon:t.jsx(Y,{fontSize:"small"}),sx:{mr:j=>j.spacing(.5)},checked:b&&!S,indeterminate:S})}),o.title]},p)},f=(p,h)=>{const o=h.inputValue.trim(),b=qe(p,{...h,inputValue:o}),S=p.some(j=>o===j.title);return o.length>=2&&!S&&b.push({inputValue:o,title:`Create new value "${o}"`}),b};return t.jsx(L,{multiple:!0,id:"checkboxes-tag",sx:{marginTop:p=>p.spacing(2),width:500},disableCloseOnSelect:!0,options:e,value:a,isOptionEqualToValue:(p,h)=>h.inputValue&&h.inputValue!==""?p.title===h.inputValue:p.title===h.title,getOptionLabel:u,renderOption:g,filterOptions:f,ListboxProps:{style:{maxHeight:200,overflow:"auto"}},onChange:r,renderInput:p=>t.jsx(M,{...p,label:"Select values",placeholder:"Select values"}),disabled:l})},Be=w("li")({flexDirection:"column"}),ht=({options:e,value:a,disabled:s=!1,onChange:i})=>{const d=ee();return t.jsx(L,{disablePortal:!0,disabled:s,id:"tag-type-select",sx:{marginTop:l=>l.spacing(2),width:500},options:e,disableClearable:!0,value:a,getOptionLabel:l=>l.name,renderOption:({key:l,...r},c)=>t.jsxs(Be,{...r,style:{alignItems:"flex-start",gap:d.spacing(.5)},children:[t.jsx(E,{variant:"body1",children:c.name}),t.jsx(E,{variant:"caption",children:c.description})]},l),renderInput:l=>t.jsx(M,{...l,label:"Tag type",value:a}),onChange:i,ListboxProps:{style:{maxHeight:200,overflow:"auto"}}})},gt=()=>{const{makeRequest:e,createRequest:a,errors:s,loading:i}=te({propagateErrors:!0});return{createTag:async r=>{const u=a("api/admin/tags",{method:"POST",body:JSON.stringify(r)});return e(u.caller,u.id)},bulkUpdateTags:async(r,c)=>{const u=`api/admin/projects/${c}/tags`,g=a(u,{method:"PUT",body:JSON.stringify(r)});return e(g.caller,g.id)},errors:s,loading:i}},mt=(e,a={})=>{const s=async()=>{const g=N(`api/admin/tags/${e}`);return(await fetch(g,{method:"GET"}).then(V("Tags"))).json()},i=`api/admin/tags/${e}`,{data:d,error:l}=se(!!e,{tags:[]},i,s,a),[r,c]=v.useState(!l&&!d),u=()=>{ae(i)};return v.useEffect(()=>{c(!l&&!d)},[d,l]),{tags:(d==null?void 0:d.tags)||[],error:l,loading:r,refetch:u}},Ue=(e,a)=>{var r,c;const{project:s,refetch:i}=z(e),d={name:"flexibleRollout",constraints:[],parameters:{rollout:"100",stickiness:s.defaultStickiness||"default",groupId:""}},l=(c=(r=s.environments)==null?void 0:r.find(u=>u.environment===a))==null?void 0:c.defaultStrategy;return{defaultStrategyFallback:d,strategy:l,refetch:i}},ft=()=>{const e=G("projectId"),a=ne("environmentId"),{refetch:s}=z(e),{defaultStrategyFallback:i,strategy:d,refetch:l}=Ue(e,a),[r,c]=v.useState(d||i),{updateDefaultStrategy:u,loading:g}=re(),{strategyDefinition:f}=oe(r.name),{setToastData:p,setToastApiError:h}=R(),o=le(),{uiConfig:b}=ie(),{unleashUrl:S}=b,j=ce(),{trackEvent:A}=de(),I=ue(r.constraints);if(!f)return null;const T=Le(r),n=async()=>{const C=`/projects/${e}/settings/default-strategy`;try{await u(e,a,T),A("default_strategy",{props:{action:"edit",hasTitle:!!T.title}}),s(),p({text:"Default Strategy updated",type:"success"}),await l(),j(C)}catch(F){h(k(F))}};return t.jsx(pe,{modal:!0,disablePadding:!0,description:Ne,documentationLink:Ve,documentationLinkLabel:ze,formatApiCode:()=>Me(e,a,T,f,S),children:t.jsxs(he,{strategy:r,setStrategy:c,strategyDefinition:f,errors:o,groupIdTooltip:"Defaults to the feature flag name if not set.",onSubmit:n,children:[t.jsx(D,{permission:[me,fe],projectId:e,environmentId:a,variant:"contained",color:"primary",type:"submit",disabled:g||!I||o.hasFormErrors(),"data-testid":ge,children:"Save strategy"}),t.jsx(be,{type:"button",onClick:()=>j(`/projects/${e}/settings/default-strategy`),children:"Cancel"})]})})},Le=e=>({name:e.name,title:e.title,constraints:e.constraints??[],parameters:Object.fromEntries(Object.entries(e.parameters??{}).filter(([,a])=>a!==void 0).map(([a,s])=>[a,String(s)])),variants:e.variants??[],segments:e.segments??[],disabled:e.disabled??!1}),Me=(e,a,s,i,d)=>{if(!d)return"";const l={...s,parameters:ve(s.parameters??{},i)},r=`${d}/api/admin/projects/${e}/environments/${a}/default-strategy`,c=JSON.stringify(l,void 0,2);return`curl --location --request POST '${r}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${c}'`},Ne=`
An activation strategy will only run when a feature flag is enabled and provides a way to control who will get access to the feature.
If any of a feature flag's activation strategies returns true, the user will get access.
`,Ve="https://docs.getunleash.io/concepts/projects#project-default-strategy",ze="Default strategy documentation",bt=e=>{const{data:a,error:s,mutate:i}=ye(N(`api/admin/environments/project/${e}`),Ge),d=v.useMemo(()=>a||[],[a]),l=v.useCallback(async()=>{await i()},[i]);return{environments:d,refetchEnvironments:l,loading:!s&&!a,error:s}},Ge=async e=>(await fetch(e).then(V("Environments")).then(s=>s.json())).environments.sort((s,i)=>s.sortOrder-i.sortOrder),He=e=>{const{setToastData:a,setToastApiError:s}=R(),{addChange:i}=je(),{refetch:d}=Se(e),[l,r]=v.useState(!1),[c,u]=v.useState({isOpen:!1}),g=v.useCallback((h,o,b,S)=>{u({featureName:h,environment:o,enabled:b,shouldActivateDisabledStrategies:S,isOpen:!0})},[]),f=v.useCallback(()=>{u(h=>({...h,isOpen:!1}))},[]),p=v.useCallback(async()=>{try{r(!0),await i(e,c.environment,{feature:c.featureName,action:"updateEnabled",payload:{enabled:!!c.enabled,shouldActivateDisabledStrategies:!!c.shouldActivateDisabledStrategies}}),d(),u(h=>({...h,isOpen:!1})),a({type:"success",text:"Changes added to draft"})}catch(h){s(k(h)),u(o=>({...o,isOpen:!1}))}finally{r(!1)}},[i]);return{pending:l,onChangeRequestToggle:g,onChangeRequestToggleClose:f,onChangeRequestToggleConfirm:p,changeRequestDialogDetails:c}},Je=({enabled:e,featureName:a,environment:s})=>t.jsxs(E,{"data-testid":"update-enabled-message",children:[t.jsx("strong",{children:e?"Enable":"Disable"})," feature flag"," ",t.jsx("strong",{children:a})," in ",t.jsx("strong",{children:s})]}),Ye=w("ul")(({theme:e})=>({margin:e.spacing(1),paddingLeft:e.spacing(2)})),Ke=({isOpen:e,onAddDefaultStrategy:a,onActivateDisabledStrategies:s,onClose:i,environment:d,featureId:l})=>{var f,p,h;const r=G("projectId"),{feature:c}=Te(r,l),u=(h=(p=(f=c.environments)==null?void 0:f.find(({name:o})=>o===d))==null?void 0:p.strategies)==null?void 0:h.filter(({disabled:o})=>o).length,g=u?u===1?"1 disabled strategy":`${u} disabled strategies`:"disabled strategies";return t.jsxs(H,{open:e,secondaryButtonText:"Cancel",permissionButton:t.jsxs(t.Fragment,{children:[t.jsx(D,{type:"button",variant:"outlined",permission:q,projectId:r,environmentId:d,onClick:a,children:"Add default strategy"}),t.jsx(D,{type:"button",variant:"outlined",permission:q,projectId:r,environmentId:d,onClick:s,children:"Enable all strategies"})]}),onClose:i,title:`Enable feature flag in ${d}`,fullWidth:!0,children:[t.jsx(E,{sx:{mb:o=>o.spacing(3)},children:"A feature flag cannot be enabled without an enabled strategy."}),t.jsx(E,{children:"To enable this feature flag you can choose to:"}),t.jsxs(Ye,{children:[t.jsx("li",{children:t.jsx(E,{children:t.jsx("strong",{children:"Add the default strategy"})})}),t.jsx("li",{children:t.jsxs(E,{children:[t.jsx("strong",{children:"Enable all the disabled strategies"})," ","(this feature flag has ",g,")"]})})]})]})},We=e=>{const a=s=>{s<e.length&&e[s](()=>a(s+1))};a(0)},vt=e=>{const{toggleFeatureEnvironmentOn:a,toggleFeatureEnvironmentOff:s}=J(),{setToastData:i,setToastApiError:d}=R(),[l,r]=v.useState({open:!1,label:"",loading:!1,onClose:()=>{},onClick:()=>{}}),[c,u]=v.useState({isOpen:!1,environment:"",featureId:"",onClose:()=>{},onActivateDisabledStrategies:()=>{},onAddDefaultStrategy:()=>{}}),{pending:g,onChangeRequestToggle:f,onChangeRequestToggleClose:p,onChangeRequestToggleConfirm:h,changeRequestDialogDetails:o}=He(e),[b,S]=v.useState(),j=v.useCallback(async(T,n)=>{let C=!1;return We([y=>{if(n.isChangeRequestEnabled||!Ce(n.environmentType||""))return y();r({open:!0,label:`${T?"Enable":"Disable"} Environment`,loading:!1,onClose:()=>{var m;r(x=>({...x,open:!1})),(m=n.onRollback)==null||m.call(n)},onClick:()=>{r(m=>({...m,open:!1,loading:!0})),y()}})},y=>{if(T===!1||!n.hasStrategies||n.hasEnabledStrategies||n.hasReleasePlans)return y();u({isOpen:!0,environment:n.environmentName,featureId:n.featureId,onClose:()=>{var m;u(x=>({...x,isOpen:!1})),(m=n.onRollback)==null||m.call(n)},onActivateDisabledStrategies:()=>{u(m=>({...m,isOpen:!1})),C=!0,y()},onAddDefaultStrategy:()=>{u(m=>({...m,isOpen:!1})),y()}})},y=>{if(!n.isChangeRequestEnabled)return y();S(()=>{var m;S(void 0),(m=n.onRollback)==null||m.call(n)}),f(n.featureId,n.environmentName,T,C)},async y=>{var m,x;if(T!==!1)return y();try{await s(n.projectId,n.featureId,n.environmentName),i({type:"success",text:`Disabled in ${n.environmentName}`}),(m=n.onSuccess)==null||m.call(n)}catch(O){d(k(O)),(x=n.onRollback)==null||x.call(n)}},async y=>{var m,x;if(T!==!0)return y();try{await a(n.projectId,n.featureId,n.environmentName,C),i({type:"success",text:`Enabled in ${n.environmentName}`}),(m=n.onSuccess)==null||m.call(n)}catch(O){d(k(O)),(x=n.onRollback)==null||x.call(n)}}])},[r]),A=c.featureId.length!==0,I=t.jsxs(t.Fragment,{children:[t.jsx(xe,{...l}),t.jsx(P,{condition:A,show:t.jsx(Ke,{...c})}),t.jsx(Ee,{isOpen:o.isOpen,onClose:()=>{b==null||b(),p()},environment:o==null?void 0:o.environment,disabled:g,onConfirm:()=>{b==null||b(),h()},messageComponent:t.jsx(Je,{enabled:o==null?void 0:o.enabled,featureName:o==null?void 0:o.featureName,environment:o.environment})})]});return{onToggle:j,modals:I}},Qe=e=>{const[a,s]=v.useState(e),i=v.useCallback(()=>s(e),[e]);return v.useEffect(()=>{s(e)},[e]),[a,s,i]},Xe=w(Ae)(()=>({mx:"auto",...Ie})),yt=({projectId:e,featureId:a,environmentName:s,value:i,onToggle:d})=>{const[l,r,c]=Qe(i),u=()=>{r(!l),requestAnimationFrame(()=>{d(!l,c)})},g=`${a}-${s}`;return t.jsx(t.Fragment,{children:t.jsx(Xe,{"data-testid":`TOGGLE-${g}`,children:t.jsx(ke,{tooltip:l?`Disable flag in ${s}`:`Enable flag in ${s}`,checked:i,environmentId:s,projectId:e,permission:Re,inputProps:{"aria-label":s},onClick:u,"data-testid":"permission-switch",disabled:i!==l})},g)})};var $={},Ze=U;Object.defineProperty($,"__esModule",{value:!0});var et=$.default=void 0,tt=Ze(B()),st=t,at=(0,tt.default)((0,st.jsx)("path",{d:"M16 11h-1V3c0-1.1-.9-2-2-2h-2c-1.1 0-2 .9-2 2v8H8c-2.76 0-5 2.24-5 5v7h18v-7c0-2.76-2.24-5-5-5zm3 10h-2v-3c0-.55-.45-1-1-1s-1 .45-1 1v3h-2v-3c0-.55-.45-1-1-1s-1 .45-1 1v3H9v-3c0-.55-.45-1-1-1s-1 .45-1 1v3H5v-5c0-1.65 1.35-3 3-3h8c1.65 0 3 1.35 3 3v5z"}),"CleaningServices");et=$.default=at;const nt="flag-reminders:v1",rt=50,ot=7,jt=({days:e=ot,maxReminders:a=rt}={})=>{const[s,i]=Oe(nt,{});return{shouldShowReminder:r=>{const c=s[r];return!c||Pe(new Date,new Date(c))},snoozeReminder:(r,c=e)=>{const u=De(new Date,c).getTime();i(g=>{const f={...g,[r]:u},p=Object.entries(f);if(p.length>a){p.sort((o,b)=>o[1]-b[1]);const h=p.slice(p.length-a);return Object.fromEntries(h)}return f})}}},St=({isStale:e,isOpen:a,projectId:s,featureId:i,onClose:d})=>{const{setToastData:l,setToastApiError:r}=R(),{patchFeatureToggle:c}=J(),u=t.jsx(E,{children:"Setting a flag to stale marks it for cleanup"}),g=t.jsx(E,{children:"Setting a flag to active marks it as in active use"}),f=e?"active":"stale",p=async h=>{h.stopPropagation();try{await c(s,i,[{op:"replace",path:"/stale",value:!e}]),d()}catch(o){r(k(o))}l(e?{type:"success",text:"The flag is no longer marked as stale"}:{type:"success",text:"The flag has been marked as stale"})};return t.jsx(H,{open:a,secondaryButtonText:"Cancel",primaryButtonText:`Flip to ${f}`,title:`Set feature state to ${f}`,onClick:p,onClose:d,children:t.jsx(P,{condition:e,show:g,elseShow:u})})};export{ft as E,yt as F,ht as T,mt as a,pt as b,Ue as c,bt as d,vt as e,jt as f,et as g,St as h,gt as u};