github.com/argoproj/argo-cd@v1.8.7/ui/src/app/shared/services/applications-service.ts (about) 1 import * as deepMerge from 'deepmerge'; 2 import {Observable} from 'rxjs'; 3 4 import * as models from '../models'; 5 import requests from './requests'; 6 7 interface QueryOptions { 8 fields: string[]; 9 exclude?: boolean; 10 selector?: string; 11 } 12 13 function optionsToSearch(options?: QueryOptions) { 14 if (options) { 15 return {fields: (options.exclude ? '-' : '') + options.fields.join(','), selector: options.selector || ''}; 16 } 17 return {}; 18 } 19 20 export class ApplicationsService { 21 public list(projects: string[], options?: QueryOptions): Promise<models.ApplicationList> { 22 return requests 23 .get('/applications') 24 .query({project: projects, ...optionsToSearch(options)}) 25 .then(res => res.body as models.ApplicationList) 26 .then(list => { 27 list.items = (list.items || []).map(app => this.parseAppFields(app)); 28 return list; 29 }); 30 } 31 32 public get(name: string, refresh?: 'normal' | 'hard'): Promise<models.Application> { 33 const query: {[key: string]: string} = {}; 34 if (refresh) { 35 query.refresh = refresh; 36 } 37 return requests 38 .get(`/applications/${name}`) 39 .query(query) 40 .then(res => this.parseAppFields(res.body)); 41 } 42 43 public getApplicationSyncWindowState(name: string): Promise<models.ApplicationSyncWindowState> { 44 return requests 45 .get(`/applications/${name}/syncwindows`) 46 .query({name}) 47 .then(res => res.body as models.ApplicationSyncWindowState); 48 } 49 50 public revisionMetadata(name: string, revision: string): Promise<models.RevisionMetadata> { 51 return requests.get(`/applications/${name}/revisions/${revision || 'HEAD'}/metadata`).then(res => res.body as models.RevisionMetadata); 52 } 53 54 public resourceTree(name: string): Promise<models.ApplicationTree> { 55 return requests.get(`/applications/${name}/resource-tree`).then(res => res.body as models.ApplicationTree); 56 } 57 58 public watchResourceTree(name: string): Observable<models.ApplicationTree> { 59 return requests.loadEventSource(`/stream/applications/${name}/resource-tree`).map(data => JSON.parse(data).result as models.ApplicationTree); 60 } 61 62 public managedResources(name: string, options: {id?: models.ResourceID; fields?: string[]} = {}): Promise<models.ResourceDiff[]> { 63 return requests 64 .get(`/applications/${name}/managed-resources`) 65 .query({...options.id, fields: (options.fields || []).join(',')}) 66 .then(res => (res.body.items as any[]) || []) 67 .then(items => { 68 items.forEach(item => { 69 if (item.liveState) { 70 item.liveState = JSON.parse(item.liveState); 71 } 72 if (item.targetState) { 73 item.targetState = JSON.parse(item.targetState); 74 } 75 if (item.predictedLiveState) { 76 item.predictedLiveState = JSON.parse(item.predictedLiveState); 77 } 78 if (item.normalizedLiveState) { 79 item.normalizedLiveState = JSON.parse(item.normalizedLiveState); 80 } 81 }); 82 return items as models.ResourceDiff[]; 83 }); 84 } 85 86 public getManifest(name: string, revision: string): Promise<models.ManifestResponse> { 87 return requests 88 .get(`/applications/${name}/manifests`) 89 .query({name, revision}) 90 .then(res => res.body as models.ManifestResponse); 91 } 92 93 public updateSpec(appName: string, spec: models.ApplicationSpec): Promise<models.ApplicationSpec> { 94 return requests 95 .put(`/applications/${appName}/spec`) 96 .send(spec) 97 .then(res => res.body as models.ApplicationSpec); 98 } 99 100 public update(app: models.Application): Promise<models.Application> { 101 return requests 102 .put(`/applications/${app.metadata.name}`) 103 .send(app) 104 .then(res => this.parseAppFields(res.body)); 105 } 106 107 public create(app: models.Application): Promise<models.Application> { 108 return requests 109 .post(`/applications`) 110 .send(app) 111 .then(res => this.parseAppFields(res.body)); 112 } 113 114 public delete(name: string, cascade: boolean): Promise<boolean> { 115 return requests 116 .delete(`/applications/${name}`) 117 .query({cascade}) 118 .send({}) 119 .then(() => true); 120 } 121 122 public watch(query?: {name?: string; resourceVersion?: string}, options?: QueryOptions): Observable<models.ApplicationWatchEvent> { 123 const search = new URLSearchParams(); 124 if (query) { 125 if (query.name) { 126 search.set('name', query.name); 127 } 128 if (query.resourceVersion) { 129 search.set('resourceVersion', query.resourceVersion); 130 } 131 } 132 if (options) { 133 const searchOptions = optionsToSearch(options); 134 search.set('fields', searchOptions.fields); 135 search.set('selector', searchOptions.selector); 136 } 137 const searchStr = search.toString(); 138 const url = `/stream/applications${(searchStr && '?' + searchStr) || ''}`; 139 return requests 140 .loadEventSource(url) 141 .repeat() 142 .retry() 143 .map(data => JSON.parse(data).result as models.ApplicationWatchEvent) 144 .map(watchEvent => { 145 watchEvent.application = this.parseAppFields(watchEvent.application); 146 return watchEvent; 147 }); 148 } 149 150 public sync(name: string, revision: string, prune: boolean, dryRun: boolean, strategy: models.SyncStrategy, resources: models.SyncOperationResource[]): Promise<boolean> { 151 return requests 152 .post(`/applications/${name}/sync`) 153 .send({revision, prune: !!prune, dryRun: !!dryRun, strategy, resources}) 154 .then(() => true); 155 } 156 157 public rollback(name: string, id: number): Promise<boolean> { 158 return requests 159 .post(`/applications/${name}/rollback`) 160 .send({id}) 161 .then(() => true); 162 } 163 164 public getContainerLogs(applicationName: string, namespace: string, podName: string, containerName: string): Observable<models.LogEntry> { 165 const entries = requests 166 .loadEventSource(`/applications/${applicationName}/pods/${podName}/logs?container=${containerName}&follow=true&namespace=${namespace}`) 167 .map(data => JSON.parse(data).result as models.LogEntry); 168 return new Observable(observer => { 169 const subscription = entries.subscribe( 170 entry => { 171 if (entry.last) { 172 observer.complete(); 173 subscription.unsubscribe(); 174 } else { 175 observer.next(entry); 176 } 177 }, 178 err => observer.error(err), 179 () => observer.complete() 180 ); 181 return () => subscription.unsubscribe(); 182 }); 183 } 184 185 public getResource(name: string, resource: models.ResourceNode): Promise<models.State> { 186 return requests 187 .get(`/applications/${name}/resource`) 188 .query({ 189 name: resource.name, 190 namespace: resource.namespace, 191 resourceName: resource.name, 192 version: resource.version, 193 kind: resource.kind, 194 group: resource.group 195 }) 196 .then(res => res.body as {manifest: string}) 197 .then(res => JSON.parse(res.manifest) as models.State); 198 } 199 200 public getResourceActions(name: string, resource: models.ResourceNode): Promise<models.ResourceAction[]> { 201 return requests 202 .get(`/applications/${name}/resource/actions`) 203 .query({ 204 namespace: resource.namespace, 205 resourceName: resource.name, 206 version: resource.version, 207 kind: resource.kind, 208 group: resource.group 209 }) 210 .then(res => (res.body.actions as models.ResourceAction[]) || []); 211 } 212 213 public runResourceAction(name: string, resource: models.ResourceNode, action: string): Promise<models.ResourceAction[]> { 214 return requests 215 .post(`/applications/${name}/resource/actions`) 216 .query({ 217 namespace: resource.namespace, 218 resourceName: resource.name, 219 version: resource.version, 220 kind: resource.kind, 221 group: resource.group 222 }) 223 .send(JSON.stringify(action)) 224 .then(res => (res.body.actions as models.ResourceAction[]) || []); 225 } 226 227 public patchResource(name: string, resource: models.ResourceNode, patch: string, patchType: string): Promise<models.State> { 228 return requests 229 .post(`/applications/${name}/resource`) 230 .query({ 231 name: resource.name, 232 namespace: resource.namespace, 233 resourceName: resource.name, 234 version: resource.version, 235 kind: resource.kind, 236 group: resource.group, 237 patchType 238 }) 239 .send(JSON.stringify(patch)) 240 .then(res => res.body as {manifest: string}) 241 .then(res => JSON.parse(res.manifest) as models.State); 242 } 243 244 public deleteResource(applicationName: string, resource: models.ResourceNode, force: boolean): Promise<any> { 245 return requests 246 .delete(`/applications/${applicationName}/resource`) 247 .query({ 248 name: resource.name, 249 namespace: resource.namespace, 250 resourceName: resource.name, 251 version: resource.version, 252 kind: resource.kind, 253 group: resource.group, 254 force 255 }) 256 .send() 257 .then(() => true); 258 } 259 260 public events(applicationName: string): Promise<models.Event[]> { 261 return requests 262 .get(`/applications/${applicationName}/events`) 263 .send() 264 .then(res => (res.body as models.EventList).items || []); 265 } 266 267 public resourceEvents( 268 applicationName: string, 269 resource: { 270 namespace: string; 271 name: string; 272 uid: string; 273 } 274 ): Promise<models.Event[]> { 275 return requests 276 .get(`/applications/${applicationName}/events`) 277 .query({ 278 resourceUID: resource.uid, 279 resourceNamespace: resource.namespace, 280 resourceName: resource.name 281 }) 282 .send() 283 .then(res => (res.body as models.EventList).items || []); 284 } 285 286 public terminateOperation(applicationName: string): Promise<boolean> { 287 return requests 288 .delete(`/applications/${applicationName}/operation`) 289 .send() 290 .then(() => true); 291 } 292 293 private parseAppFields(data: any): models.Application { 294 data = deepMerge( 295 { 296 apiVersion: 'argoproj.io/v1alpha1', 297 kind: 'Application', 298 spec: { 299 project: 'default' 300 }, 301 status: { 302 resources: [], 303 summary: {} 304 } 305 }, 306 data 307 ); 308 309 return data as models.Application; 310 } 311 }