001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.console.views;
018
019import java.util.*;
020
021import org.apache.juneau.commons.bean.*;
022
023/**
024 * The response shape of an {@code aggregate}-mode bulk action ({@link RowAction.BulkMode#AGGREGATE}) —
025 * one outcome per id submitted in the matching {@link BulkRequest}.
026 *
027 * <h5 class='section'>Own, independently-versioned contract</h5>
028 * <p>
029 * {@link #CONTRACT_VERSION} is this type's own wire contract, never aliased to {@code VIEW}, {@code ACTION_RESULT},
030 * or {@link BulkMutateDef#CONTRACT_VERSION} &mdash; each of those versions what it defines (a view's shape, a
031 * single-row action's outcome, a bulk-mutate action list) and this one versions only this response shape.
032 *
033 * <h5 class='section'>Example:</h5>
034 * <p class='bjava'>
035 *    <ja>@RestPost</ja>(path=<js>"/changes/abort"</js>)
036 *    <jk>public</jk> BulkResult abortChanges(<ja>@Content</ja> BulkRequest <jv>request</jv>) {
037 *       BulkResult.Builder <jv>result</jv> = BulkResult.<jsm>create</jsm>();
038 *       <jk>for</jk> (String <jv>id</jv> : <jv>request</jv>.ids) {
039 *          Change <jv>change</jv> = <jv>changeStore</jv>.find(<jv>id</jv>);
040 *          <jk>if</jk> (<jv>change</jv> == <jk>null</jk>) {
041 *             <jv>result</jv>.notFound(<jv>id</jv>);
042 *          } <jk>else if</jk> (!<js>"PENDING"</js>.equals(<jv>change</jv>.state)) {
043 *             <jv>result</jv>.failed(<jk>new</jk> BulkResult.Failure(<jv>id</jv>, <js>"Change is already merged."</js>));
044 *          } <jk>else</jk> {
045 *             <jv>changeStore</jv>.abort(<jv>id</jv>);
046 *             <jv>result</jv>.succeeded(<jv>id</jv>);
047 *          }
048 *       }
049 *       <jk>return</jk> <jv>result</jv>.build();
050 *    }
051 * </p>
052 *
053 * @since 10.0.0
054 */
055@BeanType(properties="contractVersion,succeeded,notFound,failed")
056public final class BulkResult {
057
058   /** This type's own wire contract version. */
059   public static final String CONTRACT_VERSION = "1";
060
061   /**
062    * One failed-outcome entry: the id that was submitted, and why it failed.
063    *
064    * @param id The submitted id.
065    * @param message Why it failed.
066    * @since 10.0.0
067    */
068   public record Failure(String id, String message) {}
069
070   /** The frozen contract-version discriminator (always {@value #CONTRACT_VERSION}). */
071   public final String contractVersion;
072
073   /** The ids that succeeded. */
074   public final List<String> succeeded;
075
076   /** The ids that were submitted but no longer exist. */
077   public final List<String> notFound;
078
079   /** The ids that failed, with a reason each. */
080   public final List<Failure> failed;
081
082   private BulkResult(Builder b) {
083      contractVersion = CONTRACT_VERSION;
084      succeeded = List.copyOf(b.succeeded);
085      notFound = List.copyOf(b.notFound);
086      failed = List.copyOf(b.failed);
087   }
088
089   /**
090    * Starts a new {@link Builder}.
091    *
092    * @return A new, empty builder.
093    */
094   public static Builder create() {
095      return new Builder();
096   }
097
098   /**
099    * This result's contract version.
100    *
101    * @return {@link #CONTRACT_VERSION}.
102    */
103   public String contractVersion() {
104      return contractVersion;
105   }
106
107   /**
108    * The ids that succeeded.
109    *
110    * @return An immutable list. Never <jk>null</jk>.
111    */
112   public List<String> succeeded() {
113      return succeeded;
114   }
115
116   /**
117    * The ids that were submitted but no longer exist (e.g. deleted between selection and submit).
118    *
119    * @return An immutable list. Never <jk>null</jk>.
120    */
121   public List<String> notFound() {
122      return notFound;
123   }
124
125   /**
126    * The ids that failed, with a reason each.
127    *
128    * @return An immutable list. Never <jk>null</jk>.
129    */
130   public List<Failure> failed() {
131      return failed;
132   }
133
134   /**
135    * A mutable builder for {@link BulkResult}.
136    *
137    * @since 10.0.0
138    */
139   public static final class Builder {
140
141      private final List<String> succeeded = new ArrayList<>();
142      private final List<String> notFound = new ArrayList<>();
143      private final List<Failure> failed = new ArrayList<>();
144
145      private Builder() {}
146
147      /**
148       * Appends ids to the succeeded list.
149       *
150       * @param ids The ids. May be called more than once; each call appends.
151       * @return This builder.
152       */
153      public Builder succeeded(String...ids) {
154         succeeded.addAll(Arrays.asList(ids));
155         return this;
156      }
157
158      /**
159       * Appends ids to the not-found list.
160       *
161       * @param ids The ids. May be called more than once; each call appends.
162       * @return This builder.
163       */
164      public Builder notFound(String...ids) {
165         notFound.addAll(Arrays.asList(ids));
166         return this;
167      }
168
169      /**
170       * Appends failures to the failed list.
171       *
172       * @param failures The failures. May be called more than once; each call appends.
173       * @return This builder.
174       */
175      public Builder failed(Failure...failures) {
176         failed.addAll(Arrays.asList(failures));
177         return this;
178      }
179
180      /**
181       * Builds the immutable {@link BulkResult}.
182       *
183       * @return A new {@link BulkResult}.
184       */
185      public BulkResult build() {
186         return new BulkResult(this);
187      }
188   }
189}