@@ -27,11 +27,11 @@ import {
2727 EnvironmentModel ,
2828 FLAGSMITH_APP ,
2929 FeatureModel ,
30- FeatureStateValue ,
30+ EnvironmentFeatureState ,
3131 FlagModel ,
3232 fetchEnvironments ,
3333 fetchFeatures ,
34- fetchFlags ,
34+ fetchFeatureState ,
3535} from "./flagsmith" ;
3636import { canEditIssue , readFeatureIds , readProjectId , writeFeatureIds } from "./jira" ;
3737import { readApiKey , readOrganisationId } from "./storage" ;
@@ -40,13 +40,15 @@ import { readApiKey, readOrganisationId } from "./storage";
4040ForgeUI ;
4141
4242type IssueFlagFormProps = {
43- features : FeatureModel [ ] ;
44- featureIds : string [ ] ;
43+ flagsmithFeatures : FeatureModel [ ] ;
44+ jiraFeatureIds : string [ ] ;
4545 onAdd : ( featureId : string ) => Promise < void > ;
4646} ;
4747
48- const IssueFlagForm = ( { features, featureIds, onAdd } : IssueFlagFormProps ) => {
49- const addableFeatures = features . filter ( ( feature ) => ! featureIds . includes ( String ( feature . id ) ) ) ;
48+ const IssueFlagForm = ( { flagsmithFeatures, jiraFeatureIds, onAdd } : IssueFlagFormProps ) => {
49+ const addableFeatures = flagsmithFeatures . filter (
50+ ( feature ) => ! jiraFeatureIds . includes ( String ( feature . id ) ) ,
51+ ) ;
5052
5153 return (
5254 < Fragment >
@@ -71,45 +73,86 @@ const IssueFlagForm = ({ features, featureIds, onAdd }: IssueFlagFormProps) => {
7173
7274type IssueFlagTableProps = {
7375 projectUrl : string ;
76+ apiKey : string ;
7477 environments : EnvironmentModel [ ] ;
75- features : FeatureModel [ ] ;
76- flags : Record < string , FlagModel [ ] > ;
77- featureIds : string [ ] ;
78+ flagsmithFeatures : FeatureModel [ ] ;
79+ jiraFeatureIds : string [ ] ;
7880 onRemove : ( featureId : string ) => Promise < void > ;
7981 canEdit : boolean ;
8082} ;
8183
8284const IssueFlagTable = ( {
8385 projectUrl,
86+ apiKey,
8487 environments,
85- features,
86- flags,
87- featureIds,
88+ flagsmithFeatures,
89+ jiraFeatureIds,
8890 onRemove,
8991 canEdit,
9092} : IssueFlagTableProps ) => {
91- if ( featureIds . length === 0 ) {
93+ const [ environmentFlags , setEnvironmentFlags ] = useState < any > ( [ ] ) ;
94+
95+ useEffect ( async ( ) => {
96+ // Filtered features by comparing their IDs with the feature IDs stored in Jira.
97+ const flagsmithfeaturesFiltered = flagsmithFeatures . filter ( ( f ) =>
98+ jiraFeatureIds . includes ( String ( f . id ) ) ,
99+ ) ;
100+ try {
101+ if ( environments . length > 0 && flagsmithfeaturesFiltered . length > 0 ) {
102+ const featureState : any = { } ;
103+ // Iterate over each filtered feature.
104+ for ( const feature of flagsmithfeaturesFiltered ) {
105+ // Initialize an object to store the state of the feature.
106+ featureState [ String ( feature . name ) ] = {
107+ name : feature . name ,
108+ feature_id : feature . id ,
109+ description : feature . description ,
110+ environments : [ ] ,
111+ } ;
112+ for ( const environment of environments ) {
113+ // Obtain the feature states of the filtered features.
114+ const ffData = await fetchFeatureState ( {
115+ apiKey,
116+ featureName : feature . name ,
117+ envAPIKey : String ( environment . api_key ) ,
118+ } ) ;
119+ ffData . name = environment . name ;
120+ ffData . api_key = String ( environment . api_key ) ;
121+ // Add the feature state data to the feature state object.
122+ featureState [ String ( feature . name ) ] . environments . push ( ffData ) ;
123+ }
124+ }
125+ const ffArray = Object . keys ( featureState ) . map ( ( featureName ) => featureState [ featureName ] ) ;
126+ setEnvironmentFlags ( ffArray ) ;
127+ } else {
128+ setEnvironmentFlags ( [ ] ) ;
129+ }
130+ } catch ( error ) {
131+ if ( ! ( error instanceof Error ) ) throw error ;
132+ }
133+ } , [ apiKey , jiraFeatureIds , environments , flagsmithFeatures ] ) ;
134+
135+ if ( jiraFeatureIds . length === 0 ) {
92136 return < Text > No feature flags are linked to this issue.</ Text > ;
93137 }
94138
95139 let first = true ;
96140 return (
97141 < Fragment >
98- { featureIds . map ( ( featureId ) => {
99- const feature = features . find ( ( each ) => String ( each . id ) === String ( featureId ) ) ;
142+ { environmentFlags . map ( ( environmentFlag : FlagModel ) => {
100143 // add vertical space to separate the previous feature's Remove button from this feature
101144 const spacer = first ? null : < Text > </ Text > ;
102145 first = false ;
103146 return (
104- ! ! feature && (
105- < Fragment key = { featureId } >
147+ ! ! environmentFlag && (
148+ < Fragment key = { environmentFlag . feature_id } >
106149 { canEdit && spacer }
107150 < Text >
108151 < Strong >
109- { feature . name }
110- { feature . description ? ": " : "" }
152+ { environmentFlag . name }
153+ { environmentFlag . description ? ": " : "" }
111154 </ Strong >
112- { feature . description }
155+ { environmentFlag . description }
113156 </ Text >
114157 < Table >
115158 < Head >
@@ -126,36 +169,30 @@ const IssueFlagTable = ({
126169 < Text > Last updated</ Text >
127170 </ Cell >
128171 </ Head >
129- { environments . map ( ( environment ) => {
130- const environmentFlags = flags [ String ( environment . id ) ] ?? [ ] ;
131- // get the default state for this environment
132- const flag = environmentFlags . find (
133- ( each ) =>
134- String ( each . feature ) === String ( featureId ) &&
135- each . feature_segment === null &&
136- each . identity === null ,
137- ) ;
172+ { environmentFlag ?. environments . map ( ( flag : EnvironmentFeatureState ) => {
138173 if ( ! flag ) return null ;
139- const value : Partial < FeatureStateValue > = flag . feature_state_value ?? { } ;
140174 // count variations/overrides
141175 const variations = flag . multivariate_feature_state_values . length ;
142176 const segments = environmentFlags . filter (
143- ( each ) =>
144- String ( each . feature ) === String ( featureId ) && each . feature_segment !== null ,
177+ ( each : EnvironmentFeatureState ) =>
178+ String ( each . feature ) === String ( environmentFlag . feature_id ) &&
179+ each . feature_segment !== null ,
145180 ) . length ;
146181 const identities = environmentFlags . filter (
147- ( each ) => String ( each . feature ) === String ( featureId ) && each . identity !== null ,
182+ ( each : EnvironmentFeatureState ) =>
183+ String ( each . feature ) === String ( environmentFlag . feature_id ) &&
184+ each . identity !== null ,
148185 ) . length ;
149186 return (
150- < Row key = { String ( featureId ) } >
187+ < Row key = { String ( ` ${ environmentFlag . feature_id } ` ) } >
151188 < Cell >
152189 < Text >
153190 < Link
154- href = { `${ projectUrl } /environment/${ environment . api_key } /features?feature=${ featureId } ` }
191+ href = { `${ projectUrl } /environment/${ flag . api_key } /features?feature=${ environmentFlag . feature_id } ` }
155192 appearance = "link"
156193 openNewTab
157194 >
158- { environment . name }
195+ { flag . name }
159196 </ Link >
160197 </ Text >
161198 { variations > 0 && (
@@ -192,15 +229,7 @@ const IssueFlagTable = ({
192229 </ Text >
193230 </ Cell >
194231 < Cell >
195- { value . type === "unicode" ? (
196- < Text > { JSON . stringify ( value . string_value ) } </ Text >
197- ) : value . type === "int" ? (
198- < Text > { JSON . stringify ( value . integer_value ) } </ Text >
199- ) : value . type === "bool" ? (
200- < Text > { JSON . stringify ( ! ! value . boolean_value ) } </ Text >
201- ) : (
202- < Text > Unknown type: { value . type } </ Text >
203- ) }
232+ < Text > { flag . feature_state_value } </ Text >
204233 </ Cell >
205234 < Cell >
206235 < Text >
@@ -213,7 +242,10 @@ const IssueFlagTable = ({
213242 </ Table >
214243 { canEdit && (
215244 < ButtonSet >
216- < Button text = "Unlink from issue" onClick = { ( ) => onRemove ( featureId ) } />
245+ < Button
246+ text = "Unlink from issue"
247+ onClick = { ( ) => onRemove ( `${ environmentFlag . feature_id } ` ) }
248+ />
217249 </ ButtonSet >
218250 ) }
219251 </ Fragment >
@@ -224,15 +256,13 @@ const IssueFlagTable = ({
224256 ) ;
225257} ;
226258
227- type Flags = Record < string , FlagModel [ ] > ;
228-
229259type IssueFlagPanelProps = {
230260 setError : ( error : Error ) => void ;
231261 jiraContext : JiraContext ;
232262 apiKey : string ;
233263 organisationId : string ;
234264 projectId : string | undefined ;
235- featureIds : string [ ] | undefined ;
265+ jiraFeatureIds : string [ ] | undefined ;
236266 canEdit : boolean ;
237267} ;
238268
@@ -245,11 +275,9 @@ const IssueFlagPanel = ({
245275 ...props
246276} : IssueFlagPanelProps ) => {
247277 // set initial state
248- const [ featureIds , setFeatureIds ] = useState ( props . featureIds ?? [ ] ) ;
278+ const [ jiraFeatureIds , setJiraFeatureIds ] = useState ( props . jiraFeatureIds ?? [ ] ) ;
249279 const [ environments , setEnvironments ] = useState ( [ ] as EnvironmentModel [ ] ) ;
250280 const [ features , setFeatures ] = useState ( [ ] as FeatureModel [ ] ) ;
251- const [ flags , setFlags ] = useState ( { } as Flags ) ;
252-
253281 // load environments and features
254282 useEffect ( async ( ) => {
255283 try {
@@ -263,47 +291,37 @@ const IssueFlagPanel = ({
263291 ) ;
264292 // update form state
265293 setFeatures ( features ) ;
266- if ( environments . length > 0 && features . length > 0 ) {
267- // obtain flags from API
268- const flags : Flags = { } ;
269- for ( const environment of environments ) {
270- flags [ String ( environment . id ) ] = await fetchFlags ( {
271- apiKey,
272- environmentId : String ( environment . id ) ,
273- } ) ;
274- }
275- // update form state
276- setFlags ( flags ) ;
277- }
278294 } catch ( error ) {
279295 if ( ! ( error instanceof Error ) ) throw error ;
280296 setError ( error ) ;
281297 }
282298 } , [ apiKey , String ( projectId ) ] ) ;
283299
284- const onChange = async ( featureIds : string [ ] ) => {
300+ const onChange = async ( jiraFeatureIds : string [ ] ) => {
285301 // persist to storage
286- await writeFeatureIds ( jiraContext , featureIds ) ;
302+ await writeFeatureIds ( jiraContext , jiraFeatureIds ) ;
287303 // update state
288- setFeatureIds ( featureIds ) ;
304+ setJiraFeatureIds ( jiraFeatureIds ) ;
289305 } ;
290- const onAdd = ( featureId : string ) => onChange ( [ ...featureIds , featureId ] ) ;
306+ const onAdd = ( featureId : string ) => onChange ( [ ...jiraFeatureIds , featureId ] ) ;
291307 const onRemove = ( featureId : string ) =>
292- onChange ( featureIds . filter ( ( each ) => String ( each ) !== featureId ) ) ;
308+ onChange ( jiraFeatureIds . filter ( ( each ) => String ( each ) !== featureId ) ) ;
293309
294310 const projectUrl = `${ FLAGSMITH_APP } /project/${ projectId } ` ;
295311 return (
296312 < Fragment >
297313 < IssueFlagTable
298314 projectUrl = { projectUrl }
315+ apiKey = { apiKey }
299316 environments = { environments }
300- features = { features }
301- flags = { flags }
302- featureIds = { featureIds }
317+ flagsmithFeatures = { features }
318+ jiraFeatureIds = { jiraFeatureIds }
303319 onRemove = { onRemove }
304320 canEdit = { canEdit }
305321 />
306- { canEdit && < IssueFlagForm features = { features } featureIds = { featureIds } onAdd = { onAdd } /> }
322+ { canEdit && (
323+ < IssueFlagForm flagsmithFeatures = { features } jiraFeatureIds = { jiraFeatureIds } onAdd = { onAdd } />
324+ ) }
307325 </ Fragment >
308326 ) ;
309327} ;
@@ -329,9 +347,9 @@ export default () => {
329347 const [ organisationId , setOrganisationId ] = useState ( readOrganisationId ) ;
330348 const jiraContext = useJiraContext ( ) ;
331349 const [ projectId , setProjectId ] = useState ( ( ) => readProjectId ( jiraContext ) ) ;
332- const [ featureIds , setFeatureIds ] = useState ( ( ) => readFeatureIds ( jiraContext ) ) ;
350+ const [ jiraFeatureIds , setJiraFeatureIds ] = useState ( ( ) => readFeatureIds ( jiraContext ) ) ;
333351 const [ canEdit , setCanEdit ] = useState ( ( ) => canEditIssue ( jiraContext ) ) ;
334- const [ editing , setEditing ] = useState ( ! ( featureIds ?? [ ] ) . length ) ;
352+ const [ editing , setEditing ] = useState ( ! ( jiraFeatureIds ?? [ ] ) . length ) ;
335353
336354 const actions = canEdit
337355 ? [ < EditAction key = "edit" editing = { editing } setEditing = { setEditing } /> ]
@@ -346,15 +364,15 @@ export default () => {
346364 jiraContext = { jiraContext }
347365 organisationId = { organisationId }
348366 projectId = { projectId }
349- featureIds = { featureIds }
367+ jiraFeatureIds = { jiraFeatureIds }
350368 canEdit = { canEdit && editing }
351369 />
352370 ) }
353371 onRetry = { async ( ) => {
354372 setApiKey ( await readApiKey ( ) ) ;
355373 setOrganisationId ( await readOrganisationId ( ) ) ;
356374 setProjectId ( await readProjectId ( jiraContext ) ) ;
357- setFeatureIds ( await readFeatureIds ( jiraContext ) ) ;
375+ setJiraFeatureIds ( await readFeatureIds ( jiraContext ) ) ;
358376 setCanEdit ( await canEditIssue ( jiraContext ) ) ;
359377 } }
360378 />
0 commit comments